docs: start milestone v1.1 WXRead 深化对齐

This commit is contained in:
shen 2026-05-22 13:59:26 +08:00
parent 1498e83ace
commit bd4978266a
52 changed files with 211 additions and 3968 deletions

View File

@ -8,6 +8,16 @@
稳定可用的 EPUB/TXT 阅读体验。
## Current Milestone: v1.1 WXRead 深化对齐
**Goal:** 在 v1.0 已完成的 native reflowable 基础上,继续补齐页面几何能力、自定义分页属性闭环、分页质量/缓存,以及更强的自动化验证。
**Target features:**
- native text `layout frame` 几何查询与命中能力
- WXRead 风格分页属性从 HTML/CSS 到 paginator 的闭环
- 复杂图文章节分页质量与缓存/性能采样
- native reflowable 主路径自动化或稳定半自动化回归
## Current State
- 已完成 `v1.0`:现有 reflowable EPUB native path 已完成一轮 WXRead 风格原生化演进。

76
.planning/REQUIREMENTS.md Normal file
View File

@ -0,0 +1,76 @@
# 需求ReadViewSDK v1.1
**定义日期:** 2026-05-22
**核心价值:** 稳定可用的 EPUB/TXT 阅读体验
## v1.1 需求(本次范围)
### 页面级布局与几何能力
- [ ] **LAYOUT-01**:为 native text 分页结果补齐页面级 layout frame 几何查询能力,至少包括字符串范围矩形查询、矩形反查文本范围、截断检测、命中定位所需的基础 API
- [ ] **LAYOUT-02**:阅读器侧的高亮、搜索命中、选区、点击定位等行为应优先建立在 layout frame 几何能力之上,而不是继续强依赖 `UITextView` 的黑盒行为
- [ ] **LAYOUT-03**:页面级模型需要继续保留并兼容 `pageStartOffset`、`pageEndOffset`、`fragmentOffsets`、`RDEPUBTextOffsetRangeInfo` 等既有语义
### WXRead 自定义属性闭环
- [ ] **ATTR-01**:补齐一条从章节 HTML / CSS 到 `NSAttributedString` 再到分页器的自定义属性闭环,至少覆盖 `avoidPageBreakInside`、`pageBreakBefore`、`pageBreakAfter`、`pageRelate`
- [ ] **ATTR-02**:图片与附件相关排版需要支持类似 `wr-vertical-center-style` 的语义输入,并在分页 / 展示阶段保留可消费的信息
- [ ] **ATTR-03**:复杂块元素至少要能区分代码块、表格、列表、引用块等类别,以支持更精细的分页边界控制与诊断
### 分页质量与性能收敛
- [ ] **QUAL-01**:为 layout frame / 分页结果增加缓存机制,避免同一章节在相同视口与排版配置下重复全量排版
- [ ] **QUAL-02**:复杂图文章节的分页质量要进一步提升,减少不合理断页、孤行/寡行、图片前后留白异常、代码块/表格被粗暴截断等问题
- [ ] **QUAL-03**:图片与附件的尺寸策略、暗色模式适配、页面背景类信息需要具备可验证的处理规则,而不是依赖默认 DTCoreText 行为
- [ ] **QUAL-04**:分页深化后不能显著恶化首屏时间、重分页耗时或交互流畅度;若无法立即建立硬性指标,至少要输出稳定的诊断与采样数据
### 稳定性与自动化验证
- [ ] **AUTO-01**:建立覆盖 native reflowable 主路径的自动化回归能力,至少覆盖打开书籍、分页完成、搜索命中、主题/字号切换、位置恢复
- [ ] **AUTO-02**:样本矩阵需要持续覆盖复杂图文、代码/表格/列表、附件密集、fixed/interactive WebKit、TXT 五类路径
- [ ] **AUTO-03**每项核心能力都应尽量附着到可重复证据日志、断言、诊断摘要、UI 自动化步骤或稳定的人工检查清单
## Future Requirements
### 后续增强(暂不进入 v1.1 roadmap
- **PERF-01**:建立更严格的性能基线与持续采样门槛(首屏时间、重分页时间、内存峰值)
- **GEOM-01**:如果几何层成熟,进一步评估是否需要逐步替换 `UITextView` 展示层
- **INTL-01**更完整的国际化排版增强RTL、断字、多语种高级回退
## Out of Scope
| 功能 | 原因 |
|------|------|
| 重写或替换 `RDReaderView` 翻页容器 | 本次继续只深化 native reflowable 内核与页面几何能力 |
| 复刻 WXRead 的完整业务层能力(翻译/双语、免费试读、网络协议、DRM | 这些属于业务闭环,不是当前 SDK 与 WXRead 的主要技术差距 |
| 将 fixed layout / interactive EPUB 改为原生渲染 | 仍保持 `WKWebView` 路径以控制风险 |
| 直接拷贝微信读书私有 CSS / JS / 私有实现代码 | 仅参考设计思路,不直接搬运私有实现 |
| 一次性完整重写成自绘 `WRPageView` 等价体系 | 风险过大,优先通过 layout frame 几何层与局部 reader 接线演进 |
## Traceability
| Requirement | Phase | Status |
|-------------|-------|--------|
| LAYOUT-01 | Phase 6 | Pending |
| LAYOUT-02 | Phase 6 | Pending |
| LAYOUT-03 | Phase 6 | Pending |
| ATTR-01 | Phase 7 | Pending |
| ATTR-02 | Phase 7 | Pending |
| ATTR-03 | Phase 7 | Pending |
| QUAL-01 | Phase 8 | Pending |
| QUAL-02 | Phase 8 | Pending |
| QUAL-03 | Phase 8 | Pending |
| QUAL-04 | Phase 8 | Pending |
| AUTO-01 | Phase 9 | Pending |
| AUTO-02 | Phase 9 | Pending |
| AUTO-03 | Phase 9 | Pending |
**Coverage:**
- v1.1 requirements: 13 total
- Mapped to phases: 13
- Unmapped: 0 ✓
---
*Requirements defined: 2026-05-22*
*Last updated: 2026-05-22 after v1.1 milestone initialization*

View File

@ -1,3 +1,103 @@
# 路线图ReadViewSDK
# 路线图ReadViewSDK v1.1
- `v1.0` 已归档:见 [v1.0-ROADMAP.md](/Users/shen/Work/Code/ReadViewSDK/.planning/milestones/v1.0-ROADMAP.md);下一步请启动新 milestone 并生成新的 requirements / roadmap。
## 概述
`v1.1` 聚焦在 v1.0 已完成的 native reflowable EPUB 基础上继续深化 WXRead 对齐,不重新打开更大范围的架构重写。实施顺序固定为:先补页面级 layout frame 几何能力,再建立自定义分页属性闭环,然后收敛分页质量/缓存,最后把 native reflowable 的回归证据提升到自动化或稳定半自动化。`RDReaderView` 继续视为稳定容器fixed layout / interactive EPUB 继续保留 `WKWebView` 路径。
## Phases
- [ ] **Phase 6: 页面几何与交互命中层** - 为 native text 建立可复用的 layout frame 几何能力,并让 reader 交互优先依赖该几何层
- [ ] **Phase 7: WXRead 自定义属性闭环** - 建立章节 HTML/CSS → attributed string → paginator 的自定义分页属性闭环
- [ ] **Phase 8: 分页质量、缓存与性能采样** - 改善复杂图文分页质量并加入缓存、暗色/附件规则和性能诊断
- [ ] **Phase 9: 自动化回归与证据标准化** - 把 native reflowable 主路径回归能力提升到自动化或稳定半自动化,并扩展样本矩阵
## Phase Details
### Phase 6: 页面几何与交互命中层
**Goal**: 为 native text 分页结果建立页面级 layout frame 几何查询能力,并把 reader 交互逐步迁移到显式几何层,而不是继续主要依赖 `UITextView` 的黑盒行为。
**Depends on**: Phase 5
**Requirements**: LAYOUT-01, LAYOUT-02, LAYOUT-03
**Success Criteria** (必须为 TRUE):
1. native text 页面可查询字符串范围矩形、矩形反查文本范围、截断与命中信息
2. 至少一条 reader 交互链路(如搜索命中、高亮或点击定位)优先消费 layout frame 几何结果
3. `pageStartOffset`、`pageEndOffset`、`fragmentOffsets`、`RDEPUBTextOffsetRangeInfo` 兼容语义保持稳定
**Plans**: 3 plans
Plans:
- [ ] 06-01`RDEPUBTextLayoutFrame` / page model 补齐几何查询 API 与截断诊断
- [ ] 06-02让 reader 的命中/定位链路优先消费 layout frame 几何层
- [ ] 06-03验证 offset / fragment 兼容契约在新几何层下不回归
Cross-cutting constraints:
- 不替换 `RDReaderView`
- 不引入新的公开 reader 入口
- 优先增量接线,不一次性重写展示层
### Phase 7: WXRead 自定义属性闭环
**Goal**: 把章节 HTML/CSS 中的分页相关语义稳定地传导到 attributed string 与 paginator让复杂块元素、图片和附件拥有更精细的分页控制。
**Depends on**: Phase 6
**Requirements**: ATTR-01, ATTR-02, ATTR-03
**Success Criteria** (必须为 TRUE):
1. 至少 `avoidPageBreakInside`、`pageBreakBefore`、`pageBreakAfter`、`pageRelate` 形成端到端闭环
2. 图片/附件垂直居中等语义在分页或展示阶段仍可被消费
3. 代码块、表格、列表、引用块等类别在分页诊断中可区分,并影响分页边界控制
**Plans**: 3 plans
Plans:
- [ ] 07-01定义并接线分页相关自定义属性从 HTML/CSS 到 attributed string 的映射
- [ ] 07-02补齐图片/附件语义与页面消费规则
- [ ] 07-03为复杂块元素分类与分页规则建立诊断和验证路径
Cross-cutting constraints:
- 继续沿用现有 chapter preprocessing 与 renderer contract
- 不直接搬运微信读书私有实现
- 新属性必须可诊断、可回归,不是隐式魔法行为
### Phase 8: 分页质量、缓存与性能采样
**Goal**: 在新几何层与属性闭环之上提升复杂图文章节分页质量,并控制重复排版开销与主题/附件规则的一致性。
**Depends on**: Phase 7
**Requirements**: QUAL-01, QUAL-02, QUAL-03, QUAL-04
**Success Criteria** (必须为 TRUE):
1. 相同视口与排版配置下,同一章节不会重复全量排版
2. 复杂图文章节分页质量可见改善,减少粗暴截断、不合理留白、孤行/寡行
3. 图片尺寸策略、暗色模式适配、页面背景类信息有明确处理规则和诊断证据
4. 首屏时间、重分页耗时或交互流畅度不会出现明显退化,且有稳定采样数据
**Plans**: 3 plans
Plans:
- [ ] 08-01为 layout frame / pagination 增加缓存键与失效策略
- [ ] 08-02针对复杂样本收敛分页质量与图片/附件/背景规则
- [ ] 08-03补充性能采样与质量诊断输出
Cross-cutting constraints:
- 先做缓存和局部优化,再考虑进一步扩展特性面
- 质量提升必须基于真实样本,而不是仅靠 synthetic case
- 保持与现有 offset-based consumer 的兼容
### Phase 9: 自动化回归与证据标准化
**Goal**: 建立覆盖 native reflowable 主路径的自动化或稳定半自动化回归能力,并把样本矩阵和运行时证据标准化。
**Depends on**: Phase 8
**Requirements**: AUTO-01, AUTO-02, AUTO-03
**Success Criteria** (必须为 TRUE):
1. native reflowable 至少覆盖打开书籍、分页完成、搜索命中、主题/字号切换、位置恢复
2. 样本矩阵持续覆盖复杂图文、代码/表格/列表、附件密集、fixed/interactive WebKit、TXT 五类路径
3. 每项核心能力都具备可重复证据日志、断言、诊断摘要、UI 自动化步骤或稳定人工检查清单
**Plans**: 2 plans
Plans:
- [ ] 09-01建立 native reflowable 关键交互自动化或稳定半自动化验证
- [ ] 09-02扩展样本矩阵并统一证据标准、日志摘要与 rerun 清单
Cross-cutting constraints:
- 不为验证再造第二套 demo shell
- 优先复用 `ReadViewDemo`、现有日志和诊断输出
- WebKit / TXT 路径仍必须留在矩阵内,不能只测 native reflowable
## Progress
| Phase | Plans Complete | Status | Completed |
|---|---:|---|---|
| 6. 页面几何与交互命中层 | 0/3 | Not started | - |
| 7. WXRead 自定义属性闭环 | 0/3 | Not started | - |
| 8. 分页质量、缓存与性能采样 | 0/3 | Not started | - |
| 9. 自动化回归与证据标准化 | 0/2 | Not started | - |

View File

@ -1,16 +1,16 @@
---
gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
status: ready_for_new_milestone
last_updated: 2026-05-22T05:40:00.000Z
milestone: v1.1
milestone_name: WXRead 深化对齐
status: planning
last_updated: "2026-05-22T05:54:18.839Z"
last_activity: 2026-05-22
progress:
total_phases: 0
total_phases: 4
completed_phases: 0
total_plans: 0
total_plans: 11
completed_plans: 0
percent: 100
stopped_at: v1.0 archived with accepted audit gaps — ready to start next milestone
percent: 0
---
# STATE
@ -23,18 +23,18 @@ stopped_at: v1.0 archived with accepted audit gaps — ready to start next miles
参见:`.planning/PROJECT.md`(更新于 2026-05-22
**核心价值:** 稳定可用的 EPUB/TXT 阅读体验
**当前关注:** `v1.0` 已归档,下一步启动 `v1.1` 里程碑定义
**当前关注:** `v1.1` 已初始化,下一步进入 Phase 6 讨论与规划
## 当前结论(摘要)
- `v1.0` 已完成归档,详细路线图与需求快照见 `.planning/milestones/`。
- 当前项目已拥有可运行的 native reflowable EPUB 主路径,并保留 fixed/interactive `WKWebView` 与 TXT 兼容能力
- `v1.1` 候选需求已草拟在 `.planning/REQUIREMENTS-v1.1-WXRead-next.md`
- `v1.1` 已基于现有候选需求正式生成 active `REQUIREMENTS.md``ROADMAP.md`。
- 当前活跃 roadmap 包含 Phase 6-9页面几何、属性闭环、分页质量/缓存、自动化回归
- `v1.0` 归档与 audit 仍保留在 `.planning/milestones/``.planning/v1.0-MILESTONE-AUDIT.md` 供后续追溯
## 当前风险焦点
- 里程碑归档接受了 requirement bookkeeping gaps详见 `.planning/v1.0-MILESTONE-AUDIT.md`
- native text 几何查询、自定义分页属性闭环、分页质量与自动化验证仍是下一里程碑主要风险
- native text 几何查询、自定义分页属性闭环、分页质量与自动化验证是 `v1.1` 的主要技术风险
- `v1.0` 遗留的 requirement bookkeeping gap 已归档,不应阻塞 `v1.1` 执行,但后续引用历史证据时需注意
- simulator 仍会输出既有 `.SFUI-Semibold` CoreText 替代提示,但未造成功能性失败
## 现有代码库地图
@ -50,11 +50,14 @@ stopped_at: v1.0 archived with accepted audit gaps — ready to start next miles
## 下一步
- 建议下一步:`$gsd-new-milestone`
- 建议下一步:`$gsd-discuss-phase 6`
- 已完成的产物目录:
- `.planning/phases/01-current-engine-boundaries/`
- `.planning/phases/02-typesetter-css/`
- `.planning/phases/03-page-metadata-pagination/`
- `.planning/phases/04-reader-capabilities/`
- `.planning/phases/05-regression-stability/`
- `.planning/milestones/`
- `.planning/REQUIREMENTS-v1.1-WXRead-next.md`
## Current Position
Phase: 6
Plan: —
Status: Ready for discuss-phase
Last activity: 2026-05-22 — Milestone v1.1 requirements and roadmap created

View File

@ -1,102 +0,0 @@
---
phase: 01-current-engine-boundaries
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md
autonomous: true
requirements:
- COMP-02
user_setup: []
must_haves:
truths:
- 审计文档准确描述 `RDURLReaderController -> RDEPUBReaderController -> readingProfile -> text/native or web` 的真实分流链路。
- 审计文档明确 `webFixedLayout``webInteractive` 继续走 `WKWebView`,不纳入原生重构。
- 审计文档明确 `RDReaderView` 是必须保持稳定的翻页容器。
artifacts:
- .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md
key_links:
- `RDEPUBReaderController.paginatePublication()``RDEPUBParser.readingProfile()` 的关系被写清楚。
- `RDEPUBTextBookBuilder` / `RDEPUBDTCoreTextRenderer` / `RDEPUBTextContentView` 之间的数据流被写清楚。
---
<objective>
产出一份基于当前代码的 `.textReflowable` 审计文档。
Purpose: 为后续 Phase 2/3 提供“当前旧引擎真实是什么”的事实基线,避免围绕错误边界重构。
Output: `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md`
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-current-engine-boundaries/01-RESEARCH.md
@.planning/phases/01-current-engine-boundaries/01-PATTERNS.md
@Sources/RDReaderView/RDURLReaderController.swift
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
@Sources/RDReaderView/RDReaderView.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 梳理原生 reflowable 调用链与数据模型</name>
<files>.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md</files>
<read_first>Sources/RDReaderView/RDURLReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift</read_first>
<action>新建 `01-reflowable-audit.md`,写清楚 `RDURLReaderController` 如何把 `.epub` 交给 `RDEPUBReaderController``RDEPUBParser.readingProfile()` 如何把内容分为 `textReflowable` / `webFixedLayout` / `webInteractive`,以及 `RDEPUBReaderController.paginatePublication()``textReflowable` 分支内如何调用 `RDEPUBTextBookBuilder`、`RDEPUBDTCoreTextRenderer`、`ss_pageRanges(size:)`、`RDEPUBTextBook` / `RDEPUBTextPage`。必须包含关键类型名、关键方法名和每层输入输出,不能只写概念描述。</action>
<verify>rg -n "RDURLReaderController|RDEPUBReaderController|RDEPUBTextBookBuilder|RDEPUBDTCoreTextRenderer|ss_pageRanges|RDEPUBTextBook|RDEPUBTextPage" .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md</verify>
<acceptance_criteria>
- `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` 存在
- 文档包含 `RDURLReaderController`、`RDEPUBReaderController`、`RDEPUBTextBookBuilder`、`RDEPUBDTCoreTextRenderer`、`RDEPUBTextContentView`
- 文档明确 `ss_pageRanges(size:)` 仍是当前分页核心
</acceptance_criteria>
<done>审计文档把当前 native reflowable 路径的入口、渲染、分页、展示和位置数据模型全部串起来。</done>
</task>
<task type="auto">
<name>Task 2: 写清 WebView 边界与容器不变约束</name>
<files>.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md</files>
<read_first>Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift, Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift, Sources/RDReaderView/RDReaderView.swift</read_first>
<action>在同一份审计文档中追加“边界与排除项”章节,明确 `webFixedLayout``webInteractive` 为什么继续依赖 `WKWebView``RDEPUBPaginator` 在当前架构中的职责,以及 `RDReaderView` 的 page curl / scroll / dual-page / orientation 逻辑为什么必须保持不变。把这些结论落成可以直接供后续 phase 引用的约束条目。</action>
<verify>rg -n "WKWebView|webFixedLayout|webInteractive|RDEPUBPaginator|RDReaderView|不修改" .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md</verify>
<acceptance_criteria>
- 文档包含 `WKWebView`、`webFixedLayout`、`webInteractive`、`RDEPUBPaginator`、`RDReaderView`
- 文档有单独的边界或排除项章节
- 文档明确写出“`RDReaderView` 不修改”或等价表述
</acceptance_criteria>
<done>审计文档既说明当前原生引擎,也说明哪些路径和容器不属于本次重构。</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -f .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md`
- [ ] `rg -n "textReflowable|webFixedLayout|webInteractive|RDReaderView" .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md`
- [ ] 文档内容可直接对应到当前源码文件,而不是只复述 roadmap 目标
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- 审计文档能支撑后续 Phase 2/3 不再重新摸索当前引擎边界
</success_criteria>
<output>
After completion, create `.planning/phases/01-current-engine-boundaries/01-01-SUMMARY.md`
</output>

View File

@ -1,84 +0,0 @@
---
phase: 01-current-engine-boundaries
plan: 01
subsystem: testing
tags: [epub, dtcoretext, coretext, reader, architecture]
requires: []
provides:
- "当前 `.textReflowable` 原生渲染链路审计"
- "Fixed / interactive WebView 边界说明"
- "`RDReaderView` 作为稳定翻页容器的约束结论"
affects: [phase-02, phase-03, reflowable-engine, reader-integration]
tech-stack:
added: []
patterns: ["source-first architecture audit", "reader branch-point preservation"]
key-files:
created:
- .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md
modified: []
key-decisions:
- "将 `.textReflowable` 的主战场固定在 `EPUBTextRendering`,而不是转向新的 WebView 或并行原生引擎。"
- "将 `RDEPUBTextBook` / `RDEPUBTextPage` 视为 location/highlight/search 的兼容层,不作为 Phase 2/3 的随意破坏对象。"
patterns-established:
- "先审计 `readingProfile` 分流与 reader branch point再设计引擎重构触点。"
- "以 `RDReaderView` 为稳定外壳,内核升级只在 rendering/pagination 模块内部推进。"
requirements-completed: [COMP-02]
duration: 8min
completed: 2026-05-21
---
# Phase 01 Plan 01: Current Engine Audit Summary
**审计并固定了当前 reflowable EPUB 原生引擎的真实调用链、WebView 分流边界,以及 `RDReaderView` 不可改动的容器契约**
## Performance
- **Duration:** 8 min
- **Started:** 2026-05-21T13:48:00Z
- **Completed:** 2026-05-21T13:56:09Z
- **Tasks:** 2
- **Files modified:** 1
## Accomplishments
- 写清了 `.epub``RDURLReaderController` 进入 `RDEPUBReaderController` 再进入 `readingProfile` 分流的真实入口路径
- 固定了 native `.textReflowable` 的实际渲染链路:`RDEPUBDTCoreTextRenderer` -> `RDEPUBTextBookBuilder` -> `ss_pageRanges(size:)` -> `RDEPUBTextContentView`
- 明确了 `webFixedLayout` / `webInteractive` 继续使用 `WKWebView`,并把 `RDReaderView` 记录为稳定外壳
## Task Commits
This plan was closed out with a documentation-only metadata commit after both tasks completed:
1. **Task 1: 梳理原生 reflowable 调用链与数据模型** - included in final docs commit
2. **Task 2: 写清 WebView 边界与容器不变约束** - included in final docs commit
**Plan metadata:** pending commit hash at close-out
## Files Created/Modified
- `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` - 基于源码证据的 current-engine 审计文档
## Decisions Made
- 以 `RDEPUBReaderController.paginatePublication()` 作为 native / web 分流的主证据点,而不是以历史文档描述为准
- 把 `RDEPUBPaginator` 定义为 WebView 渲染/测量体系组成部分,而不是当前 native reflowable 的分页核心
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- `gsd-sdk` 起初无法从中文 phase 名自动推导 phase 目录 slug因此本阶段使用了稳定的英文目录名 `01-current-engine-boundaries`
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Wave 1 事实基线已完成Phase 1 的 strategy plan 可以直接基于该审计文档展开
- 当前审计已足够支撑 Phase 2/3 避免把 WebView 路径和 native 路径混为一谈
---
*Phase: 01-current-engine-boundaries*
*Completed: 2026-05-21*

View File

@ -1,107 +0,0 @@
---
phase: 01-current-engine-boundaries
plan: 02
type: execute
wave: 2
depends_on:
- "01-01"
files_modified:
- .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
autonomous: true
requirements:
- COMP-02
user_setup: []
must_haves:
truths:
- 策略文档给出“旧引擎直接演进”而不是“双引擎并存”的切入方案。
- 策略文档明确 Fixed Layout / 交互式 EPUB 继续走 `WKWebView`
- 策略文档明确 `RDReaderView` 与现有翻页容器契约保持不变。
- 策略文档指出 Phase 2 与 Phase 3 分别应优先触碰的代码触点。
artifacts:
- .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
key_links:
- `Doc/WXRead/analysis/*` 的目标能力被映射到当前 `EPUBTextRendering` 触点。
- `01-reflowable-audit.md` 中确认的边界被转写为后续阶段的实施约束。
---
<objective>
基于当前审计结果和 WXRead 资料,产出一份可执行的旧引擎直接演进策略文档。
Purpose: 把“从哪里动、先动什么、哪些外部契约不能动”固定下来,作为 Phase 2/3 的实现基线。
Output: `.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md`
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-current-engine-boundaries/01-RESEARCH.md
@.planning/phases/01-current-engine-boundaries/01-PATTERNS.md
@.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md
@Doc/WXRead/analysis/01_核心架构与数据流图.md
@Doc/WXRead/analysis/02_符号恢复与核心算法.md
@Doc/WXRead/analysis/EPUB渲染管线详解.md
@Doc/WXRead/analysis/DTCoreText自定义修改分析.md
@Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md
@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/RDReaderView.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 提炼 WXRead 目标能力到当前引擎触点</name>
<files>.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md</files>
<read_first>.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md, Doc/WXRead/analysis/EPUB渲染管线详解.md, Doc/WXRead/analysis/DTCoreText自定义修改分析.md, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</read_first>
<action>新建 `01-refactor-entry-strategy.md`,把 WXRead 资料中的五层 CSS、DTCoreText 自定义属性、页面级元数据、块级避免断页、layout frame / layouter 责任拆解为当前仓库可落地的触点映射。至少给出 `RDEPUBDTCoreTextRenderer`、`RDEPUBTextBookBuilder`、`RDEPUBTextPaginationSupport`、`RDEPUBTextPage` / `RDEPUBTextChapter` 这些具体落点,并说明每个落点在 Phase 2 或 Phase 3 中承担什么职责。</action>
<verify>rg -n "RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder|RDEPUBTextPaginationSupport|RDEPUBTextPage|RDEPUBTextChapter|CSS|属性|分页" .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md</verify>
<acceptance_criteria>
- 文档包含明确的“WXRead 能力 -> 当前源码触点”映射
- 文档点名 `RDEPUBDTCoreTextRenderer`、`RDEPUBTextBookBuilder`、`RDEPUBTextPaginationSupport`
- 文档区分 Phase 2 与 Phase 3 的切入重心
</acceptance_criteria>
<done>策略文档不再停留在抽象“参考 WXRead”而是把目标能力落成当前仓库里的具体改造入口。</done>
</task>
<task type="auto">
<name>Task 2: 固化范围边界和兼容性保留链路</name>
<files>.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md</files>
<read_first>.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md, Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md, Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/RDReaderView.swift</read_first>
<action>在策略文档中追加“非目标与兼容面”章节,明确列出以下内容:`webFixedLayout` 与 `webInteractive` 保持 `WKWebView``RDReaderView` 与现有翻页模式不改,阅读位置/高亮/搜索/主题切换后的重分页必须继续通过现有 `RDEPUBReaderController` 契约工作;同时给出后续实施顺序建议,说明为什么必须先增强 renderer/typesetter再升级分页语义最后再回接 reader 能力链路。</action>
<verify>rg -n "WKWebView|RDReaderView|阅读位置|高亮|搜索|主题|重分页|实施顺序" .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md</verify>
<acceptance_criteria>
- 文档明确写出 `WKWebView` 保留边界
- 文档明确写出 `RDReaderView` 不修改
- 文档明确列出阅读位置、高亮、搜索、主题切换等兼容项
- 文档包含一个按阶段排序的切入顺序
</acceptance_criteria>
<done>策略文档既给出切入点,也把必须保留的外层契约固定为后续 phase 的硬约束。</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -f .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md`
- [ ] `rg -n "WKWebView|RDReaderView|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder" .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md`
- [ ] 文档结论与 `01-reflowable-audit.md` 不冲突,并明确拒绝双引擎方案
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- 策略文档足以直接指导 Phase 2/3 进入实现,不再重复做边界澄清
</success_criteria>
<output>
After completion, create `.planning/phases/01-current-engine-boundaries/01-02-SUMMARY.md`
</output>

View File

@ -1,87 +0,0 @@
---
phase: 01-current-engine-boundaries
plan: 02
subsystem: testing
tags: [epub, wxread, refactor, pagination, strategy]
requires:
- phase: 01-current-engine-boundaries
provides: "当前 reflowable 引擎与 WebView 边界审计"
provides:
- "旧引擎直接演进的实施顺序"
- "WXRead 能力到当前源码触点的映射"
- "后续 phase 的非目标与硬边界"
affects: [phase-02, phase-03, phase-04, refactor-sequencing]
tech-stack:
added: []
patterns: ["typesetter-first refactor", "page-model-preserving pagination upgrade"]
key-files:
created:
- .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
modified: []
key-decisions:
- "Phase 2 只升级 renderer/typesetter 与 CSS 分层,不把分页器和 reader 回接一并混入。"
- "Phase 3 再升级 `RDEPUBTextPaginationSupport`、`RDEPUBTextPage` 与 `RDEPUBTextChapter` 的页面语义。"
- "Phase 4 才处理位置、高亮、搜索、主题切换后的重分页兼容回接。"
patterns-established:
- "先提升 HTML/CSS 输入质量,再定位分页语义问题。"
- "扩展现有 `RDEPUBTextBook` 契约,而不是引入第二套 page model。"
requirements-completed: [COMP-02]
duration: 2min
completed: 2026-05-21
---
# Phase 01 Plan 02: Refactor Entry Strategy Summary
**把 WXRead 风格能力拆解成当前旧引擎可直接落地的文件触点与实施顺序,并固定了 `WKWebView``RDReaderView` 的非目标边界**
## Performance
- **Duration:** 2 min
- **Started:** 2026-05-21T13:56:10Z
- **Completed:** 2026-05-21T13:58:09Z
- **Tasks:** 2
- **Files modified:** 1
## Accomplishments
- 建立了 `WXRead capability -> current file touchpoint -> target phase` 的执行映射
- 固定了 Phase 2 先做 typesetter / CSS 分层Phase 3 再做 page model / pagination semanticsPhase 4 再做 reader compatibility 回接的实施顺序
- 把 `WKWebView` retention boundary、`RDReaderView` 不修改、位置/高亮/搜索/重分页兼容性列成后续 phase 的硬约束
## Task Commits
This plan was closed out with a documentation-only metadata commit after both tasks completed:
1. **Task 1: 提炼 WXRead 目标能力到当前引擎触点** - included in final docs commit
2. **Task 2: 固化范围边界和兼容性保留链路** - included in final docs commit
**Plan metadata:** pending commit hash at close-out
## Files Created/Modified
- `.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` - 旧引擎直接演进的切入策略文档
## Decisions Made
- 选择“typesetter first”而不是“pagination first”避免把输入质量问题和布局语义问题混在一起排查
- 把 `RDEPUBReaderController` 定义为 reader capability 编排点,而不是新的排版实现承载层
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
None
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 1 的审计与策略文档都已齐备,已能直接支撑 Phase 2 开始 renderer/typesetter 升级工作
- 后续 phase 可以把 `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md``01-refactor-entry-strategy.md` 作为固定输入
---
*Phase: 01-current-engine-boundaries*
*Completed: 2026-05-21*

View File

@ -1,31 +0,0 @@
# Phase 1: 对齐现状、边界与重构切入点 - Pattern Map
## Goal
为本阶段执行提供“先看哪里、按什么证据写文档”的最短路径。Phase 1 不改业务代码,主要产出审计和策略文档,因此模式映射聚焦在源码证据与现有方案文档之间的对应关系。
## Planned Outputs
| Planned file | Role | Primary evidence | Why this is the right analog |
|--------------|------|------------------|------------------------------|
| `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` | 当前引擎审计文档 | `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`, `Sources/RDReaderView/EPUBTextRendering/*.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift` | 这些文件构成真实 `.textReflowable` 调用链和 fixed/interactive 分流边界。 |
| `.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` | 直接演进策略文档 | `Doc/WXRead/analysis/*.md`, `Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md` | 这些文档定义了 WXRead 参考能力与当前项目已有方案草案。 |
## Code Evidence Map
| Concern | Closest source of truth | Evidence to extract |
|---------|-------------------------|---------------------|
| URL 入口如何进入阅读器 | `Sources/RDReaderView/RDURLReaderController.swift` | EPUB vs TXT 分流、`RDEPUBReaderController` 嵌入方式、TXT fallback。 |
| `.textReflowable` 何时生效 | `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift` | `.webFixedLayout` / `.webInteractive` / `.textReflowable` 判定。 |
| 原生 reflowable 渲染入口 | `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift` | `paginatePublication()` 内的 `RDEPUBTextBookBuilder` 分支。 |
| 章节 HTML 如何转富文本 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` | `DTHTMLAttributedStringBuilder`、`dtOptions`、fragment marker 注入。 |
| 章节如何切页 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` and `RDEPUBTextPaginationSupport.swift` | `ss_pageRanges(size:)`、`RDEPUBTextPage` offsets、chapter/page flattening。 |
| 文本页如何接回 reader UI | `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift` and `RDEPUBReaderController.swift` | 高亮、搜索、selection、page number、location persistence。 |
| 哪些链路必须留在 WebView | `Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift`, `RDEPUBWebView+Reflowable.swift`, `RDEPUBPaginator.swift` | Fixed-layout spread loading、interactive / web pagination、presentation script。 |
| 哪些容器代码不能动 | `Sources/RDReaderView/RDReaderView.swift` | page curl / scroll container、dual-page、orientation handling。 |
## Writing Guidance
- 审计文档优先引用源码事实,再引用 `.planning/codebase/ARCHITECTURE.md` 作为补充解释。
- 策略文档优先从 `Doc/WXRead/analysis/EPUB渲染管线详解.md`、`DTCoreText自定义修改分析.md` 提炼目标能力,再映射回当前 `EPUBTextRendering` 触点。
- 若引用旧实现差异,优先查看 `Sources/RDReaderView/LegacyRDReaderController/RDEPUBTextPaging.swift`,但不要把 legacy 行为当成自动保留项,必须与新 controller 现状对齐。

View File

@ -1,224 +0,0 @@
# Phase 1: 对齐现状、边界与重构切入点 - Research
**Researched:** 2026-05-21
**Domain:** iOS EPUB reader architecture / DTCoreText-based native reflowable rendering
**Confidence:** HIGH
<user_constraints>
## User Constraints (from CONTEXT.md)
No user constraints - all decisions at the agent's discretion.
</user_constraints>
<architectural_responsibility_map>
## Architectural Responsibility Map
| Capability | Primary Tier | Secondary Tier | Rationale |
|------------|-------------|----------------|-----------|
| EPUB URL entry and format routing | Browser/Client | — | `RDURLReaderController` decides epub vs txt and embeds the correct reader controller. |
| EPUB structure parsing and reading-profile split | Browser/Client | — | `RDEPUBParser` and `RDEPUBPublication` classify `.textReflowable` vs `webFixedLayout` / `webInteractive`. |
| Reflowable native chapter rendering | Browser/Client | — | `RDEPUBDTCoreTextRenderer` and `RDEPUBTextBookBuilder` own HTML to `NSAttributedString` and page slicing. |
| Fixed-layout and interactive content rendering | Browser/Client | — | `RDEPUBWebView` and `RDEPUBPaginator` keep `WKWebView` as the rendering and measurement path. |
| Page container and interaction shell | Browser/Client | — | `RDReaderView` is the stable presentation container that Phase 1 must not modify. |
</architectural_responsibility_map>
<research_summary>
## Summary
当前仓库已经存在一条独立的原生 reflowable 路径,而不是“所有 EPUB 都走 WebView”。`RDEPUBReaderController.paginatePublication` 会在 `publication.readingProfile == .textReflowable` 时改走 `RDEPUBTextBookBuilder`,使用 `RDEPUBDTCoreTextRenderer` 把章节 HTML 转成 `NSAttributedString`,再通过 `ss_pageRanges(size:)` 基于 CoreText 可见范围切页。该路径最终产出 `RDEPUBTextBook` / `RDEPUBTextPage`,并由 `RDEPUBTextContentView` 在现有 `RDReaderView` 容器内展示。
Fixed Layout 与交互式 EPUB 的边界已经在代码里清晰存在。`RDEPUBParser.readingProfile()` 将 `metadata.layout == .fixed` 归类为 `.webFixedLayout`,并把包含脚本/多媒体/iframe/form 等交互内容的资源归类为 `.webInteractive`。这两类内容继续走 `RDEPUBWebView` / `RDEPUBPaginator` / `WKWebView` 体系,不应被纳入本次原生重构。
WXRead 参考资料说明,目标不是“再包一层轻量 renderer”而是把当前 DTCoreText 路径升级为完整的 typesetter + layouter + layout frame 体系:五层 CSS 级联、自定义 DTCoreText 属性键、块级避免断页、页面级元数据、图片与背景等页面语义。对本仓库来说,最稳妥的切入点是保留 `RDURLReaderController -> RDEPUBReaderController -> RDReaderView` 外层契约不变,把增强集中在 `EPUBTextRendering` 内部及其与 reader state 的对接点。
**Primary recommendation:** Phase 1 应先把当前 `.textReflowable` 调用链、WebView 边界、兼容性约束和可演进触点写成明确文档,再以这些文档作为 Phase 2/3 的实现基线。
</research_summary>
<standard_stack>
## Standard Stack
### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| UIKit | iOS 15+ SDK | Reader container, controllers, text view presentation | 现有 reader UI 与 `RDReaderView` 全部建立在 UIKit 上。 |
| CoreText | System | Native pagination and text measurement | 当前 `ss_pageRanges(size:)` 直接使用 `CTFramesetterCreateFrame` / `CTFrameGetVisibleStringRange`。 |
| DTCoreText | 1.6.28 | HTML/CSS to `NSAttributedString` | 当前原生 reflowable 渲染核心已经依赖它,后续定制也必须围绕它展开。 |
| WebKit | System | Fixed-layout / interactive EPUB rendering and pagination measurement | 非 reflowable-native 路径仍依赖 `WKWebView`。 |
### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| DTFoundation | 1.7.19 | DTCoreText supporting parser/runtime pieces | DTCoreText HTML 解析与附件支持所需。 |
| ZIPFoundation | 0.9.20 | EPUB archive handling | EPUB 解压与资源访问基础设施。 |
| SnapKit | 5.7.1 | UI layout helper | Reader UI 周边布局已有依赖,但不是本次内核切入点。 |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Directly evolving `EPUBTextRendering` | A second native engine alongside the existing one | 会增加长期双轨维护成本,并破坏 roadmap 已锁定的“旧引擎直接演进”原则。 |
| DTCoreText-based native rendering | Full `WKWebView` for all EPUB modes | 无法满足 WXRead-style page metadata / page-frame semantics / native selection control goals。 |
| Keeping `RDReaderView` stable | Reworking page container and gesture stack together | 改动半径过大Phase 1 明确要求把翻页容器排除出本次重构。 |
**Installation:**
```bash
pod install
```
</standard_stack>
<architecture_patterns>
## Architecture Patterns
### System Architecture Diagram
```text
RDURLReaderController
-> RDEPUBReaderController
-> RDEPUBParser.parse()
-> RDEPUBPublication / readingProfile()
-> textReflowable
-> RDEPUBTextBookBuilder
-> RDEPUBDTCoreTextRenderer
-> DTHTMLAttributedStringBuilder
-> NSAttributedString
-> RDEPUBTextPaginationSupport.ss_pageRanges()
-> RDEPUBTextBook / RDEPUBTextPage
-> RDEPUBTextContentView
-> RDReaderView
-> webFixedLayout
-> RDEPUBWebView.loadFixedSpread()
-> RDReaderView
-> webInteractive
-> RDEPUBPaginator + RDEPUBWebView.loadPage()
-> RDReaderView
```
### Recommended Project Structure
```text
Sources/RDReaderView/
├── EPUBCore/ # Parser, publication, web pagination, render requests
├── EPUBTextRendering/ # Native reflowable rendering and pagination
├── EPUBUI/ # Reader controller, content views, settings
└── RDReaderView.swift # Stable page container and gestures
```
### Pattern 1: Preserve the outer reader contract
**What:** Keep `RDURLReaderController`, `RDEPUBReaderController`, persistence, location mapping, and `RDReaderView` as the stable shell while replacing internals inside `EPUBTextRendering`.
**When to use:** Any phase that upgrades the reflowable engine without expanding scope into the page container.
**Example:** `RDEPUBReaderController.paginatePublication` already branches into text vs web rendering without changing the reader container contract.
### Pattern 2: Keep rendering and presentation separate
**What:** Produce rich `RDEPUBTextBook` / `RDEPUBTextPage` models first, then let `RDEPUBTextContentView` render those models.
**When to use:** When introducing page-level metadata, selection offsets, image semantics, or new pagination logic.
**Example:** `RDEPUBTextBookBuilder` owns chapter rendering and page slicing, while `RDEPUBTextContentView` only paints attributed substrings and overlays highlights/search state.
### Pattern 3: Branch by publication profile early
**What:** Decide native reflowable vs fixed vs interactive before rendering work starts.
**When to use:** Any future entry-point or repagination logic.
**Example:** `RDEPUBParser.readingProfile()` uses `layout == .fixed` and interactive-content detection to keep `WKWebView` modes out of the native path.
### Anti-Patterns to Avoid
- **Touching `RDReaderView` during engine migration:** Phase 1 explicitly excludes page curl / scroll / gesture container changes.
- **Building a parallel renderer shell:** The roadmap already rejects a “new engine beside old engine” approach.
- **Mixing WebView-only semantics into the native path:** `textReflowable` should evolve around `NSAttributedString` and CoreText, not around hidden `WKWebView` dependencies.
</architecture_patterns>
<dont_hand_roll>
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| HTML parsing | A brand-new EPUB HTML parser | DTCoreText builder extension points | 仓库已经围绕 DTCoreText 建立基础能力,重写 parser 成本高且风险大。 |
| EPUB mode classification | Ad-hoc view-layer checks | `RDEPUBParser.readingProfile()` | 当前 profile 分流已经覆盖 fixed / interactive / text reflowable。 |
| Reader shell replacement | A new page container | Existing `RDReaderView` contract | 现有 container 已承担翻页模式、双页与方向逻辑,超出本次范围。 |
| Location/highlight transport | A second mapping format | Existing `RDEPUBLocation` + `RDEPUBTextOffsetRangeInfo` | 这些类型已被 highlights / search / restore location 链路依赖。 |
**Key insight:** 应该复用现有阅读器契约和数据模型,把新增复杂度集中在旧引擎内部,而不是在外围重造一层基础设施。
</dont_hand_roll>
<common_pitfalls>
## Common Pitfalls
### Pitfall 1: Confusing native reflowable with the WebView paginator
**What goes wrong:** 把 `RDEPUBPaginator` 当成所有 reflowable 的基础,导致 Phase 2/3 方案偏向继续强化 WebView。
**Why it happens:** 当前工程同时存在 `textReflowable` 和基于 `WKWebView` 的分页/展示路径。
**How to avoid:** 在所有 Phase 1 输出里明确:`RDEPUBPaginator` 服务于 fixed 或 interactive/web paths原生重构主战场是 `EPUBTextRendering`
**Warning signs:** 计划任务把 `RDEPUBPaginator` 作为 CSS 五层或页面元数据的主要落点。
### Pitfall 2: Breaking `RDReaderView` assumptions while changing pagination
**What goes wrong:** 新分页结果不再满足现有 page index / container reuse / orientation repagination 约定。
**Why it happens:** 容易把引擎升级和容器升级绑在一起处理。
**How to avoid:** 把 `RDEPUBTextBook.pages` / `RDEPUBTextPage` 视为 Phase 2/3 的兼容面,并在策略中显式约束 `RDReaderView` 不改。
**Warning signs:** 方案需要修改 `RDReaderView.DataSource`、翻页方向、双页逻辑或 content view reuse 协议。
### Pitfall 3: Introducing richer layout semantics without preserving location mapping
**What goes wrong:** 新分页器能排版复杂元素,但阅读位置、高亮、搜索结果定位失效。
**Why it happens:** 只关注排版帧,不关注 `pageStartOffset/pageEndOffset``RDEPUBLocation` 的传递。
**How to avoid:** 把 offset continuity、fragment offset、selection range transport 作为 Phase 2/3 的第一类兼容目标。
**Warning signs:** 设计里没有说明新页面元数据如何回写 `RDEPUBTextBook`、`RDEPUBSelection`、`RDEPUBHighlight`。
</common_pitfalls>
<code_examples>
## Code Examples
### Current reflowable split
```swift
if publication.readingProfile == .textReflowable {
let renderer = resolvedTextRenderer()
let builder = RDEPUBTextBookBuilder(renderer: renderer)
let textBook = try builder.build(
parser: parser,
publication: publication,
pageSize: pageSize,
style: renderStyle
)
}
```
### Current fixed vs interactive boundary
```swift
public func readingProfile() -> RDEPUBReadingProfile {
if metadata.layout == .fixed {
return .webFixedLayout
}
return hasInteractiveContent() ? .webInteractive : .textReflowable
}
```
### Current native pagination primitive
```swift
while visibleRange.location + visibleRange.length < length {
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(location, 0), path, nil)
visibleRange = CTFrameGetVisibleStringRange(frame)
ranges.append(NSRange(location: location, length: visibleRange.length))
location += visibleRange.length
}
```
</code_examples>
<sota_updates>
## State of the Art (2024-2025)
| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Generic HTML to attributed string only | HTML to attributed string plus page-level layout semantics | 已反映在 WXRead 参考资料中 | 需要把 CSS/HTML 处理与分页语义一起设计,而不是只换 builder options。 |
| Simple visible-range page slicing | Block-aware layout frames and custom pagination metadata | 已反映在 WXRead 参考资料中 | 当前 `CTFrameGetVisibleStringRange` 模式会成为主要替换对象。 |
| Styling as a small option bag | Multi-layer stylesheet cascade | 已反映在 WXRead 参考资料中 | `dtOptions` 需要被更明确的 stylesheet pipeline 取代或扩展。 |
**New tools/patterns to consider:**
- DTCoreText attribute extension points: 适合承接 WXRead 风格自定义属性键。
- Page-level layout model: 需要在 `RDEPUBTextChapter` / `RDEPUBTextPage` 附近引入更丰富的元数据,而不是只保存范围。
**Deprecated/outdated:**
- “Simple DTCoreText renderer + pageRanges” 作为长期方案:对复杂块元素、页面语义和 CSS 分层都不够。
- “为原生重构同时替换翻页容器”:与当前范围控制相冲突。
</sota_updates>
<open_questions>
## Open Questions
- `LegacyRDReaderController/RDEPUBTextPaging.swift` 与新 `EPUBTextRendering` 之间哪些实现差异仍值得迁移,哪些应直接废弃,需要在 Phase 1 执行阶段做一次并列审计。
- `Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md` 与当前源码是否已经出现偏差,需要在 Plan 01-02 中显式核对。
- 未来自定义 DTCoreText 属性应优先挂在 `NSAttributedString`、页模型,还是单独的 layout frame 对象上,需要在策略文档中做出建议但不提前定版实现。
</open_questions>

View File

@ -1,75 +0,0 @@
---
phase: 1
slug: current-engine-boundaries
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-05-21
---
# Phase 1 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | shell assertions + Xcode build smoke |
| **Config file** | none — current repo has no dedicated XCTest target for this phase |
| **Quick run command** | `test -f .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` |
| **Full suite command** | `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build` |
| **Estimated runtime** | ~60 seconds |
---
## Sampling Rate
- **After every task commit:** Run `test -f .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` or the matching strategy-doc existence/source assertions for the touched artifact.
- **After every plan wave:** Run `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build`.
- **Before `$gsd-verify-work`:** Build smoke must be green and both phase documents must exist.
- **Max feedback latency:** 60 seconds
---
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 01-01-01 | 01 | 1 | COMP-02 | — | 审计文档准确区分 `.textReflowable``WKWebView` 路径 | source | `rg -n "textReflowable|webFixedLayout|webInteractive|RDReaderView" .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` | ❌ W0 | ⬜ pending |
| 01-01-02 | 01 | 1 | COMP-02 | — | 审计文档列出真实调用链与关键源码入口 | source | `rg -n "RDURLReaderController|RDEPUBReaderController|RDEPUBTextBookBuilder|RDEPUBDTCoreTextRenderer" .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` | ❌ W0 | ⬜ pending |
| 01-02-01 | 02 | 2 | COMP-02 | — | 策略文档明确固定版式/交互式 EPUB 继续走 `WKWebView` | source | `rg -n "WKWebView|webFixedLayout|webInteractive" .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` | ❌ W0 | ⬜ pending |
| 01-02-02 | 02 | 2 | COMP-02 | — | 策略文档明确 `RDReaderView` 不修改且采用旧引擎直接演进 | source | `rg -n "RDReaderView|直接重构|不修改" .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Wave 0 Requirements
- [ ] No new test target required for Phase 1 documentation work.
- [ ] Executor must confirm `ReadViewDemo/ReadViewDemo.xcworkspace` and `ReadViewDemo` scheme still open/build before closing the phase.
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| Audit conclusions match the actual repo structure | COMP-02 | 需要阅读源码与文档交叉确认,纯自动化难以判断“结论是否误导” | 打开 `01-reflowable-audit.md`,逐项对照源码路径和 reading-profile 分流逻辑。 |
| Refactor entry strategy is actionable for Phase 2/3 | COMP-02 | 需要判断切入顺序是否真的减少风险 | 阅读 `01-refactor-entry-strategy.md`,确认触点、保留边界、后续 phase 依赖关系清晰。 |
---
## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references
- [ ] No watch-mode flags
- [ ] Feedback latency < 60s
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending

View File

@ -1,79 +0,0 @@
---
phase: 01-current-engine-boundaries
verified: 2026-05-21T13:59:30Z
status: passed
score: 4/4 must-haves verified
---
# Phase 01: Current Engine Boundaries Verification Report
**Phase Goal:** 把“当前旧引擎是什么、哪些能力必须保留、哪些路径绝对不能动”说清楚,形成直接重构旧引擎的实施基线。
**Verified:** 2026-05-21T13:59:30Z
**Status:** passed
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | 能准确描述当前 `.textReflowable` 的真实调用链与数据流 | ✓ VERIFIED | `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` 明确给出 `RDURLReaderController -> RDEPUBReaderController -> RDEPUBTextBookBuilder -> RDEPUBDTCoreTextRenderer -> ss_pageRanges(size:) -> RDEPUBTextContentView -> RDReaderView` 链路。 |
| 2 | Fixed Layout / 交互式 EPUB 的 `WKWebView` 边界不含糊,且不被纳入本次重构 | ✓ VERIFIED | 审计文档 “What Still Uses WKWebView” 与策略文档 “`WKWebView` retention boundary” 均列出 `webFixedLayout`、`webInteractive`、`RDEPUBWebView+FixedLayout.swift`、`RDEPUBWebView+Reflowable.swift`、`RDEPUBPaginator.swift`。 |
| 3 | 当前翻页代码(`RDReaderView`)被明确排除在本次改造范围外 | ✓ VERIFIED | 两份文档都把 `RDReaderView` 记录为稳定外壳;策略文档明确列出“修改 `RDReaderView` 的外部容器契约”为拒绝项。 |
| 4 | 输出一份针对“旧引擎直接演进”的重构切入策略,而不是双引擎方案 | ✓ VERIFIED | `.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` 明确给出 Phase 2 / 3 / 4 顺序和 “direct evolution, not parallel engine” 结论。 |
**Score:** 4/4 truths verified
### Required Artifacts
| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` | 当前 native reflowable 调用链与边界审计 | ✓ EXISTS + SUBSTANTIVE | 文档内容覆盖入口、分流、渲染、分页、展示、兼容层和排除项。 |
| `.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md` | 旧引擎直接演进切入策略 | ✓ EXISTS + SUBSTANTIVE | 文档内容覆盖 capability mapping、phase sequencing、兼容面和非目标。 |
| `.planning/phases/01-current-engine-boundaries/01-01-SUMMARY.md` | 审计计划执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 Wave 1 产出与决策。 |
| `.planning/phases/01-current-engine-boundaries/01-02-SUMMARY.md` | 策略计划执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 Wave 2 产出与后续 readiness。 |
**Artifacts:** 4/4 verified
### Key Link Verification
| From | To | Via | Status | Details |
|------|----|----|--------|---------|
| `01-reflowable-audit.md` | current source files | file-path evidence | ✓ WIRED | 审计文档逐段引用 `RDEPUBReaderController.swift`、`RDEPUBTextBookBuilder.swift`、`RDEPUBParser+ReadingProfile.swift`、`RDReaderView.swift`。 |
| `01-refactor-entry-strategy.md` | `01-reflowable-audit.md` | strategy derives from audit findings | ✓ WIRED | 策略文档直接以审计结论为 Phase 2/3/4 输入,避免重新定义边界。 |
| Phase 1 summaries | roadmap progress | `roadmap.update-plan-progress 01` | ✓ WIRED | `ROADMAP.md` 已显示 Phase 1 为 `2/2 | Complete | 2026-05-21`。 |
| Phase 1 requirement | requirements traceability | `requirements.mark-complete COMP-02` | ✓ WIRED | `REQUIREMENTS.md``COMP-02` 已标记为 complete。 |
**Wiring:** 4/4 connections verified
## Requirements Coverage
| Requirement | Status | Blocking Issue |
|-------------|--------|----------------|
| COMP-02: Fixed Layout EPUB 与交互式 EPUB 继续使用 `WKWebView`,行为不因本次改造产生回归 | ✓ SATISFIED | - |
**Coverage:** 1/1 requirements satisfied
## Anti-Patterns Found
None
## Human Verification Required
None — all Phase 1 deliverables are documentary and verifiable by source/document inspection.
## Gaps Summary
**No gaps found.** Phase goal achieved. Ready to proceed.
## Verification Metadata
**Verification approach:** Goal-backward, using roadmap success criteria plus plan must-haves
**Must-haves source:** Phase 1 PLAN frontmatter + ROADMAP success criteria
**Automated checks:** file existence, grep coverage, roadmap progress sync
**Human checks required:** 0
**Total verification time:** ~2 min
---
*Verified: 2026-05-21T13:59:30Z*
*Verifier: inline executor*

View File

@ -1,268 +0,0 @@
# Phase 1 Strategy: Direct Refactor Entry Points for the Existing Reflowable Engine
**Phase:** 01-current-engine-boundaries
**Date:** 2026-05-21
**Purpose:** 把“旧引擎直接演进”的切入点、顺序和硬边界固定下来,供 Phase 2/3 实施使用。
## 1. Strategy in One Sentence
后续实现应保留现有 `RDURLReaderController -> RDEPUBReaderController -> RDReaderView` 外层契约不变,把升级集中在 `EPUBTextRendering` 内部:先增强 typesetter / CSS 分层,再增强页面级属性与分页语义,最后回接并验证 reader 兼容能力。
## 2. Why the Strategy Is “Direct Evolution,” Not “Parallel Engine”
根据当前源码审计native `.textReflowable` 主路径已经具备以下骨架:
- `RDEPUBParser.readingProfile()` 已经把普通 reflowable EPUB 分到 `.textReflowable`
- `RDEPUBReaderController.paginatePublication()` 已经把 `.textReflowable` 接到 `RDEPUBTextBookBuilder`
- `RDEPUBDTCoreTextRenderer` 已经承担章节 HTML -> `NSAttributedString`
- `RDEPUBTextPaginationSupport.ss_pageRanges(size:)` 已经承担最小可用分页
- `RDEPUBTextContentView``RDReaderView` 已经承担原生页面展示
因此,新增第二套原生引擎只会制造双轨维护问题:
- duplicate reader integration
- duplicate location/highlight/search transport
- duplicate pagination data model
- duplicate regression matrix
正确策略是:**沿着现有 native text 路径升级内部能力,而不是复制一条新路径。**
## 3. Target Capability Map: WXRead -> Current Touchpoints
下表把 WXRead 目标能力映射到当前仓库中真正应该改动的触点。
| WXRead-style capability | Current touchpoint | Target phase | Why this file is the right entry point |
|-------------------------|-------------------|--------------|----------------------------------------|
| 五层 CSS 级联default / replace / dark / epub / user | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` | Phase 2 | 当前 renderer 已拥有 HTML、baseURL、style 三要素,是升级 stylesheet pipeline 的最直接入口。 |
| HTML 预处理与资源引用兜底 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` 或新建 `RDEPUBWXReadHTMLPreprocessor.swift` | Phase 2 | builder 已掌握章节原始 HTML 与章节 baseURL适合做 `<link>` CSS 内联或章节级规范化。 |
| 统一 stylesheet 生成器 | 新建 `Sources/RDReaderView/EPUBTextRendering/RDEPUBWXReadStyleSheetBuilder.swift` | Phase 2 | 避免把 layered CSS 逻辑塞进 renderer使样式层可测试、可替换、可调试。 |
| 页面级 attributed string 元数据 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`、`RDEPUBTextChapter`、`RDEPUBTextPage` | Phase 3 | 当前 page model 只有范围和 offset后续页面语义需要在这里承接。 |
| 更强分页语义(块级避免断页、图片/附件边界) | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift` | Phase 3 | 当前 `ss_pageRanges(size:)` 是唯一分页核心,增强必须从这里开始。 |
| Layout frame / layouter 责任分离 | 新建 `EPUBTextRendering` 内部 layouter/frame 类型 | Phase 3 | 不应把复杂页面布局逻辑继续堆在 `RDEPUBReaderController` 或 content view。 |
| Reader 兼容性回接(位置、高亮、搜索、重分页) | `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift` | Phase 4 | 这是现有 reader state 的唯一编排点,适合接回而不是承担新排版实现。 |
## 4. Phase 2 Entry Strategy: Upgrade Typesetter First
Phase 2 的目标不是“立刻实现 WXRead 全量布局器”,而是把渲染输入从“简单 DTCoreText options”升级为“WXRead 风格 typesetter”。
### 4.1 Primary code touchpoints
优先改动:
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
- 新建 `Sources/RDReaderView/EPUBTextRendering/RDEPUBWXReadStyleSheetBuilder.swift`
### 4.2 Concrete target state
Phase 2 应把当前 renderer 升级到以下状态:
1. 合成 layered CSS
- SDK default CSS
- SDK replace/enhancement CSS
- dark theme override
- EPUB embedded CSS
- user CSS from `RDEPUBTextRenderStyle`
2. 使用章节目录 `baseURL`
- 保留当前 baseURL 计算方式
- 确保图片与章节级 CSS 相对路径继续可解析
3. 维持 fragment marker / fragment offset 体系
- 不能破坏 `injectFragmentMarkers`
- 不能破坏 `extractFragmentOffsets`
4. 不改 reader integration surface
- `RDEPUBTextBookBuilder.build(...)` 的高层职责保持不变
- `RDEPUBReaderController.paginatePublication()` 的分支结构保持不变
### 4.3 Why Phase 2 should stop there
如果在 Phase 2 就同时引入:
- 全新分页器
- 大量新 page model
- reader compatibility 回接修复
就会把“渲染输入提升”与“分页语义重构”混为一体定位回归原因会非常困难。Phase 2 应该先把章节渲染输入变得更接近 WXRead再观察真实样本书暴露的分页问题。
## 5. Phase 3 Entry Strategy: Upgrade Pagination Semantics
Phase 3 才是“把 pageRanges 模式升级为更强页面语义”的阶段。
### 5.1 Primary code touchpoints
优先改动:
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
- `RDEPUBTextPage`
- `RDEPUBTextChapter`
- 必要时新建内部 layouter/layout-frame 类型
### 5.2 Concrete target state
Phase 3 应逐步实现:
1. 当前 range-only page model -> richer page metadata
- block boundaries
- image/attachment semantics
- page-level layout markers
- future page background or page-style metadata hooks
2. 当前 visible-range slicing -> stronger pagination semantics
- avoid page break inside for large blocks
- image and attachment boundary handling
- better page edge control than pure `CTFrameGetVisibleStringRange`
3. 保留 offset continuity
- `pageStartOffset`
- `pageEndOffset`
- chapter-level text continuity
- fragment offset compatibility
### 5.3 What not to do in Phase 3
不要把分页器升级等同于:
- 改写 `RDReaderView`
- 改写 fixed/interactive 渲染链路
- 绕开 `RDEPUBTextBook` 直接输出新的 UI 专用模型
Phase 3 的正确做法是:让更强的分页语义仍然最终落到现有 book/chapter/page 契约上,必要时是“扩展”这些模型,而不是抛弃它们。
## 6. Compatibility Surface That Must Be Preserved
以下能力必须视为后续 phase 的硬约束:
### 6.1 `WKWebView` retention boundary
保留不动:
- `webFixedLayout`
- `webInteractive`
- `RDEPUBWebView+FixedLayout.swift`
- `RDEPUBWebView+Reflowable.swift`
- `RDEPUBPaginator.swift` 所属的 WebView 测量职责
原因:
- Fixed layout 依赖 spread HTML 表示
- interactive EPUB 依赖 JS/bridge/iframe/form/multimedia 支持
- 它们不属于 native reflowable 的目标问题域
### 6.2 Stable reader container
`RDReaderView` 与现有翻页模式保持不变:
- page curl
- horizontal scroll
- vertical scroll
- page direction
- dual-page / cover page behavior
- orientation-driven container refresh
原因:
- 它是 native text 与 web content 的共同外壳
- 改它会同时扩大 regression surface
### 6.3 Reader capability continuity
以下链路必须继续通过现有 `RDEPUBReaderController` 契约工作:
- 阅读位置恢复
- `RDEPUBLocation` 映射
- 高亮 / 选区
- 搜索结果定位
- 字号/行高/主题切换后的重分页
这要求后续 phase 继续维护:
- `fragmentOffsets`
- `pageStartOffset`
- `pageEndOffset`
- `RDEPUBTextOffsetRangeInfo`
- `RDEPUBTextContentView` 所依赖的页内容语义
## 7. Recommended Implementation Sequence
建议执行顺序如下:
### Step 1: Strengthen renderer / typesetter
目标:
- layered CSS
- HTML preprocessing
- stable resource resolution
- normalized style injection
原因:
- 先把“输入质量”提升,才能判断现有分页问题到底是样式输入问题还是分页算法问题
### Step 2: Strengthen pagination semantics
目标:
- richer page metadata
- avoid-break logic
- attachment-aware page boundaries
原因:
- 在更稳定的 attributed string 输入上做分页升级,结果更可解释
### Step 3: Reconnect and validate reader capability chain
目标:
- 位置映射
- highlight/search
- repagination after settings change
原因:
- 这一步应基于已经稳定的 renderer + page model而不是把所有问题都混在一起排查
## 8. File-Level Recommendations for Next Phases
### 8.1 Files Phase 2 should read first
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
- `Doc/WXRead/analysis/EPUB渲染管线详解.md`
- `Doc/WXRead/analysis/DTCoreText自定义修改分析.md`
- `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md`
### 8.2 Files Phase 3 should read first
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
- `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`
- `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md`
- `Doc/WXRead/analysis/02_符号恢复与核心算法.md`
### 8.3 Files Phase 4 should read first
- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
- `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`
- `Sources/RDReaderView/RDReaderView.swift`
- Any Phase 2/3 summaries produced from the pagination work
## 9. Non-Goals and Refusal Rules
后续 phase 默认拒绝以下方向,除非 roadmap 被显式修改:
1. 把 Fixed Layout EPUB 改成 native renderer
2. 把 interactive EPUB 改成 native renderer
3. 新建并长期维护第二套 reflowable 原生引擎
4. 修改 `RDReaderView` 的外部容器契约
5. 在还没完成 Phase 2 renderer 升级前,就全面重写 reader integration
## 10. Final Strategy Decision
最终策略决议如下:
> Phase 2 先把 `RDEPUBDTCoreTextRenderer` 从“简单 DTCoreText options”升级成“WXRead 风格 typesetter + layered CSS pipeline”Phase 3 再升级 `RDEPUBTextPaginationSupport`、`RDEPUBTextBookBuilder` 与 page model 的页面语义Phase 4 才处理 reader capability 链路的兼容回接。整个过程中,`WKWebView` 边界和 `RDReaderView` 容器契约都保持不变。

View File

@ -1,409 +0,0 @@
# Phase 1 Audit: Current Reflowable Engine, Boundaries, and Stable Contracts
**Phase:** 01-current-engine-boundaries
**Date:** 2026-05-21
**Purpose:** 固化当前 `.textReflowable` 旧引擎的真实调用链、模式分流边界、以及本次重构不能碰的外层契约。
## 1. Executive Conclusion
当前仓库并不是“所有 EPUB 都走 `WKWebView`”。真实结构是三分流:
1. `webFixedLayout`Fixed Layout EPUB继续走 `WKWebView`
2. `webInteractive`:带脚本/多媒体/iframe/表单等交互内容的 EPUB继续走 `WKWebView`
3. `textReflowable`:普通 reflowable EPUB走 **DTCoreText -> `NSAttributedString` -> CoreText 分页 -> 原生文本页视图**
因此,本次“旧引擎直接演进”的主战场已经存在,且就在 `EPUBTextRendering`。Phase 2/3 不需要新起一套阅读器,也不需要改 `RDReaderView`;要做的是升级当前 native reflowable 的 typesetter、属性体系和分页能力。
## 2. Entry Path: URL -> Reader Controller
入口在 `Sources/RDReaderView/RDURLReaderController.swift`
### 2.1 `.epub` 入口
`RDURLReaderController.embedReaderController()` 对扩展名做分支:
- 扩展名是 `epub`
- 直接构造 `RDEPUBReaderController(epubURL:configuration:)`
- 把 reader controller 作为子控制器嵌入当前界面
这里没有直接决定 native 还是 web它只负责把 `.epub` 交给 `RDEPUBReaderController`
### 2.2 `.txt` 入口
TXT 则走 `RDPlainTextBookBuilder` 构造 `RDEPUBTextBook`,再以 external TextBook 模式交给 `RDEPUBReaderController`。这条链路与 EPUB 的原生文本展示在 UI 层会合,但不参与 EPUB reading profile 判定。
## 3. Reading Profile Split
模式分流在 `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift`
### 3.1 判定规则
`RDEPUBParser.readingProfile()` 的真实逻辑是:
- `metadata.layout == .fixed` -> `.webFixedLayout`
- 否则,如果 `hasInteractiveContent() == true` -> `.webInteractive`
- 否则 -> `.textReflowable`
### 3.2 `hasInteractiveContent()` 的判定来源
`hasInteractiveContent()` 会检查两类信号:
- manifest 中是否出现 JS 相关 media type 或 `scripted` property
- spine HTML 中是否出现交互模式特征
具体 regex 关注:
- `<script>`
- `<iframe>`
- `<video>`
- `<audio>`
- `<canvas>`
- `<svg>`
- `<form>`
- `onload` / `onclick` / `touchstart` 等内联事件
- `hype_generated_script`
- `swiper`
- `webview`
这意味着 fixed layout 与 interactive EPUB 的 WebView 边界不是概念约束,而是已经在 parser 层具备明确判定逻辑。
## 4. `.textReflowable` Real Call Chain
当前 native reflowable 的真实调用链如下:
```text
RDURLReaderController
-> RDEPUBReaderController(epubURL:)
-> RDEPUBParser
-> RDEPUBPublication
-> RDEPUBParser.readingProfile()
-> .textReflowable
-> RDEPUBReaderController.paginatePublication()
-> resolvedTextRenderer()
-> RDEPUBDTCoreTextRenderer
-> RDEPUBTextBookBuilder.build()
-> renderChapter(html:baseURL:style:)
-> RDEPUBRenderedChapterContent
-> ss_pageRanges(size:)
-> RDEPUBTextBook / RDEPUBTextChapter / RDEPUBTextPage
-> RDEPUBTextContentView
-> RDReaderView
```
### 4.1 Branch point in `RDEPUBReaderController`
`Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift``paginatePublication(restoreLocation:)` 是当前核心分支点:
- 如果 `publication.readingProfile == .textReflowable`
- 构造 renderer`resolvedTextRenderer()`
- 当前默认返回 `RDEPUBDTCoreTextRenderer()`
- 构造 builder`RDEPUBTextBookBuilder(renderer:)`
- 计算 `pageSize``RDEPUBTextRenderStyle`
- 在后台线程执行 `builder.build(...)`
- 完成后 `applyTextBook(...)`,最终由 `RDReaderView` 刷新内容
- 如果不是 `.textReflowable`
- fixed layout直接创建 fixed snapshot
- reflowable-web / interactive使用 `RDEPUBPaginator`
结论native reflowable 与 WebView 路径已经在 reader controller 内部彻底分开。
## 5. Render Stage: `RDEPUBDTCoreTextRenderer`
`Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` 是当前原生章节渲染入口。
### 5.1 输入
`renderChapter(html:baseURL:style:)` 接收:
- `html: String`
- `baseURL: URL?`
- `style: RDEPUBTextRenderStyle`
### 5.2 主要处理步骤
1. `RDEPUBTextRendererSupport.injectFragmentMarkers(into:)`
- 先往 HTML 注入 fragment marker
- 为后续目录/定位/fragment offset 映射保留锚点
2. 把 HTML 转为 UTF-8 `Data`
3. 调用 `DTHTMLAttributedStringBuilder`
- 通过 `makeAttributedString(from:baseURL:style:)`
- options 由 `dtOptions(baseURL:style:)` 提供
4. 如果 DTCoreText 构建失败
- 回退到 `fallbackRenderedContent(for:style:)`
5. 对生成的 attributed string 做后处理
- `extractFragmentOffsets(from:)`
- `normalizeReadingAttributes(in:style:)`
6. 返回 `RDEPUBRenderedChapterContent`
- `attributedString`
- `fragmentOffsets`
### 5.3 当前 `dtOptions` 的能力边界
当前 options 只有基础排版参数:
- `DTDefaultFontFamily`
- `DTDefaultFontName`
- `DTDefaultFontSize`
- `DTDefaultLineHeightMultiplier`
- `DTDefaultTextColor`
- `NSBaseURLDocumentOption`
- `DTUseiOS6Attributes`
这说明当前旧引擎的 renderer 仍然是“简单 DTCoreText builder + 少量 options”模型还没有形成 WXRead 风格的多层 stylesheet / 自定义属性 / 页面级后处理体系。
## 6. Pagination Stage: `RDEPUBTextBookBuilder`
`Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` 当前负责把章节渲染结果组织为 book/chapter/page 结构。
### 6.1 Chapter iteration
`build(parser:publication:pageSize:style:)` 会:
- 遍历 `publication.spine.enumerated()`
- 只处理 `linear == true`
- 只处理 `html` / `xhtml` 类型
- 从 parser 读取 `rawHTML`
- 计算章节标题
- `normalizeHTML(rawHTML)`
- 调用 renderer 生成 `RDEPUBRenderedChapterContent`
### 6.2 Base URL behavior
builder 把每个 spine item 的章节目录作为 `baseURL`
- `parser.fileURL(forRelativePath: item.href)?.deletingLastPathComponent()`
这意味着当前图片/CSS/相对资源的解析锚点已经是“章节所在目录”,这是 Phase 2 引入更完整 CSS 层和资源解析时必须保留的基础约束。
### 6.3 Data model produced
builder 生成以下原生分页数据结构:
- `RDEPUBTextBook`
- `chapters: [RDEPUBTextChapter]`
- `pages: [RDEPUBTextPage]`
- `RDEPUBTextChapter`
- `chapterIndex`
- `spineIndex`
- `href`
- `title`
- `attributedContent`
- `fragmentOffsets`
- `pages`
- `RDEPUBTextPage`
- `absolutePageIndex`
- `chapterIndex`
- `spineIndex`
- `href`
- `chapterTitle`
- `pageIndexInChapter`
- `totalPagesInChapter`
- `content`
- `contentRange`
- `pageStartOffset`
- `pageEndOffset`
这些模型就是当前原生引擎与 UI/状态层的契约面。
## 7. Current Core Pagination Primitive
当前分页核心在 `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- 扩展方法:`NSAttributedString.ss_pageRanges(size:)`
- 底层实现:
- `CTFramesetterCreateWithAttributedString`
- `CTFramesetterCreateFrame`
- `CTFrameGetVisibleStringRange`
处理循环逻辑是:
1. 从当前 `location` 创建 frame
2. 取 `visibleRange`
3. 把这个 range 记录为一页
4. `location += visibleRange.length`
5. 重复直到结束
### 7.1 Current limitation
这是一种典型的“可见字符串范围切页”模型,优点是简单、稳定、易于接回现有 `RDEPUBTextBook` 结构;缺点是:
- 没有块级避免断页策略
- 没有页面级语义结构
- 没有自定义 layout frame / layouter
- 很难表达复杂图片、表格、背景、分页约束
因此,`ss_pageRanges(size:)` 明确是后续需要被增强或替换的核心点之一。
## 8. Presentation Stage: `RDEPUBTextContentView`
`Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift` 是当前 native text page 的展示层。
### 8.1 Responsibilities
它负责:
- 接收单页 `RDEPUBTextPage`
- 将 `page.content` 赋给 `UITextView`
- 叠加高亮和搜索高亮
- 展示页码
- 输出 selection 和 annotation menu action
### 8.2 Why this matters
这说明当前 `.textReflowable` 路径已经和以下阅读器能力发生绑定:
- selection
- highlight
- annotate
- search highlight
- page number display
因此,如果后续升级分页器或页面语义,必须继续维护 `RDEPUBTextPage``RDEPUBTextContentView` 的兼容面,而不是绕开它另起视图层。
## 9. Location / Highlight / Search Compatibility Chain
当前原生文本路径不只是“能显示文本”,还承担已有 reader 能力链路:
### 9.1 Location mapping
`RDEPUBTextBook` 提供:
- `pageNumber(for:resolver:bookIdentifier:)`
- `location(forPageNumber:bookIdentifier:)`
它依赖:
- `fragmentOffsets`
- `pageStartOffset`
- `pageEndOffset`
- chapter-level `attributedContent.length`
### 9.2 Highlight and selection transport
`RDEPUBTextContentView` 会把选区转成:
- `RDEPUBSelection`
- `RDEPUBTextOffsetRangeInfo`
并通过 `RDEPUBReaderController` 的 text selection normalization / persistence 链路继续工作。
### 9.3 Search compatibility
`RDEPUBTextContentView.applySearchHighlights(...)` 也是基于页内 offset 重叠来计算高亮区域。
结论:`RDEPUBTextBook` 和 `RDEPUBTextPage` 不是单纯的 UI DTO而是 reader state、annotation、search、location mapping 的核心兼容层。
## 10. What Still Uses `WKWebView`
下列链路必须继续视为 WebView 范围:
### 10.1 Fixed Layout
`Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift`
- `loadFixedSpread(...)`
- `handleFixedLayoutLoad(...)`
职责:
- 把 fixed spread 渲染成 HTML
- 通过 `WKWebView.loadHTMLString(...)` 加载
- 处理 fixed spread 资源与 ready fallback
### 10.2 Interactive / web pagination support
`Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift`
职责:
- 使用离屏、非持久化 `WKWebView`
- 按 spine item 加载 HTML
- 测量文档页数
- 为 WebView 路径生成分页信息
尽管它名字叫 paginator但它不是当前 native `.textReflowable` 的分页核心;它是 WebView 渲染/测量体系的一部分。
### 10.3 Web reflowable presentation
`Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift`
职责:
- 构造 `RDEPUBReflowableRenderRequest`
- 对当前 HTML 文档应用 presentation script
- 处理 page index / target location / highlights 的 WebView 表示
这条链路应该保留给 `.webInteractive` 以及任何必须留在 WebView 的内容,不应误认为是当前 native reflowable 主路径。
## 11. Stable Container Contract: `RDReaderView`
`Sources/RDReaderView/RDReaderView.swift` 是当前阅读容器的稳定外壳。
### 11.1 What it owns
它负责:
- `pageCurl`
- `horizontalScroll`
- `verticalScroll`
- RTL / LTR 翻页方向
- landscape dual-page
- cover page pairing
- orientation change handling
- page container reuse和 data source protocol
### 11.2 Why Phase 1 must keep it unchanged
roadmap 已经把 `RDReaderView` 排除在本次改造范围外,而源码也支持这一结论:
- `RDEPUBReaderController` 只把页面数据和内容视图交给 `RDReaderView`
- native text 和 web content 都通过相同的 container contract 接入
- 改它会把内核升级变成“内核 + 容器 + 手势 + 双页策略”的多变量回归
结论:**`RDReaderView` 不修改** 是正确的硬约束,不只是项目管理偏好,而是当前架构稳定性的必要条件。
## 12. Boundary and Exclusion Rules for Next Phases
后续 phase 应默认遵守以下边界:
1. `RDEPUBParser.readingProfile()` 三分流保持不变
2. `webFixedLayout` 继续走 `WKWebView`
3. `webInteractive` 继续走 `WKWebView`
4. `.textReflowable` 继续以 `EPUBTextRendering` 为主战场
5. `RDReaderView` 不修改
6. `RDEPUBTextBook` / `RDEPUBTextChapter` / `RDEPUBTextPage` 是兼容层,后续升级不能轻易破坏
7. `fragmentOffsets`、offset-based location mapping、高亮/搜索 range transport 必须继续可用
## 13. Direct Refactor Entry Points
基于当前代码,后续“旧引擎直接演进”的直接触点应当是:
- `RDEPUBDTCoreTextRenderer`
- 从简单 `dtOptions` 升级为更完整的 stylesheet / HTML preprocessing / attribute pipeline
- `RDEPUBTextBookBuilder`
- 继续保留 chapter -> rendered content -> page model 的骨架
- 后续承接更丰富页面语义、附件/块元素信息
- `RDEPUBTextPaginationSupport`
- 从单纯 `CTFrameGetVisibleStringRange` 切页,升级到更强分页策略
- `RDEPUBTextPage` / `RDEPUBTextChapter`
- 承接页面级元数据,而不是只保存纯 range
- `RDEPUBReaderController`
- 保持 branch point 与 reader integration不把它变成新的排版实现层
## 14. Final Audit Result
Phase 1 的事实基线可以归纳为一句话:
> 当前旧引擎已经拥有一条完整的 native `.textReflowable` 路径,真正需要演进的是 `EPUBTextRendering` 内部的 renderer / page model / pagination semanticsFixed Layout、交互式 EPUB 以及 `RDReaderView` 容器都应保持边界不变。

View File

@ -1,92 +0,0 @@
---
phase: 02-typesetter-css
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextStyleSheetBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
autonomous: true
requirements:
- REND-02
user_setup: []
must_haves:
truths:
- native renderer has an explicit chapter preprocessing / stylesheet assembly layer rather than only a flat DTCoreText option bag.
- CSS precedence is deterministic and documented as `default / replace / dark / epub / user`.
- chapter baseURL and resource context are preserved through preprocessing.
artifacts:
- .planning/phases/02-typesetter-css/02-01-SUMMARY.md
key_links:
- `RDEPUBTextBookBuilder` remains the chapter boundary and provides render context.
- `RDEPUBStyleSheetBuilder` is not repurposed for the native path.
---
<objective>
设计并实现旧引擎中的 WXRead 风格 stylesheet builder / HTML 预处理增强。
Purpose: 把章节 HTML、baseURL、资源引用和用户/主题/EPUB 样式合成为一个明确的 native renderer 输入层。
Output: `Sources/RDReaderView/EPUBTextRendering/` 中新增或重构的 stylesheet / preprocessing helper以及与其对接的 renderer entry points。
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-typesetter-css/02-RESEARCH.md
@.planning/phases/02-typesetter-css/02-PATTERNS.md
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
@Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
@Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift
@Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift
@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 建立 native stylesheet / preprocessing helper</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift</read_first>
<action>`EPUBTextRendering` 内新增或重构一个明确的 native stylesheet/preprocessing helper它需要把 `RDEPUBPreferences` / `RDEPUBReaderTheme` / `RDEPUBTextRenderStyle` 里的输入翻译成可叠加的 CSS 层,并在 chapter import 前完成 HTML 规范化、style tag 组装和必要的资源上下文占位。实现时要把层级顺序写死为 `default / replace / dark / epub / user`,并把它暴露成可复用的 renderer input而不是继续在 `dtOptions` 里堆零散参数。</action>
<verify>rg -n "default|replace|dark|epub|user|stylesheet|preprocess|chapter|baseURL" Sources/RDReaderView/EPUBTextRendering</verify>
<acceptance_criteria>
- native renderer 侧出现独立的 stylesheet / preprocessing 责任
- CSS 层级顺序是显式且可读的
- chapter baseURL 与资源上下文没有被抹掉
</acceptance_criteria>
<done>章节样式输入不再是一个扁平 option bag而是一个可追踪、可验证的 native 样式层管线。</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
- [ ] `rg -n "default|replace|dark|epub|user|preprocess|stylesheet" Sources/RDReaderView/EPUBTextRendering`
- [ ] 计划文档明确拒绝复用 WebView 专用的 `RDEPUBStyleSheetBuilder`
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- native renderer 的样式层输入足以支撑 Phase 2 后半段的 renderer 接线
</success_criteria>
<output>
After completion, create `.planning/phases/02-typesetter-css/02-01-SUMMARY.md`
</output>

View File

@ -1,68 +0,0 @@
---
phase: 02-typesetter-css
plan: 01
subsystem: epub-text-rendering
tags: [epub, dtcoretext, css, stylesheet, preprocessing]
requires: []
provides:
- "native renderer 的章节级 stylesheet / preprocessing 输入层"
- "显式的 CSS 五层顺序 default / replace / dark / epub / user"
- "章节 baseURL 与资源诊断信息"
affects: [phase-02, phase-03, reflowable-engine]
tech-stack:
added: []
patterns: ["chapter render request", "stylesheet layering", "resource-aware preprocessing"]
key-files:
created: []
modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
key-decisions:
- "不复用 WebView 专用的 `RDEPUBStyleSheetBuilder`,而是在 native path 中建立独立的 chapter preprocessing pipeline。"
- "把 EPUB 外链 CSS 预处理为原位内联样式,并在内联时重写 `url(...)` 资源路径。"
patterns-established:
- "章节输入先做 HTML 规范化、baseURL 注入、stylesheet 分层,再交给 renderer。"
requirements-completed: []
duration: 35min
completed: 2026-05-22
---
# Phase 02 Plan 01: Typesetter Input Layer Summary
**为 native reflowable renderer 建立了独立的 chapter stylesheet / preprocessing 层,输入不再只是扁平 DTCoreText options。**
## Performance
- **Duration:** 35 min
- **Completed:** 2026-05-22
- **Tasks:** 1
- **Files modified:** 2
## Accomplishments
- 在 [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) 中引入了 `RDEPUBTextChapterContext`、`RDEPUBTextChapterRenderRequest`、`RDEPUBTextStyleSheetPackage` 与资源诊断模型。
- 在 [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) 中实现了章节 HTML 规范化、`<base>` 注入、linked CSS 内联、`url(...)` 重写,以及 `default / replace / dark / epub / user` 五层 CSS 组装。
- 样式层顺序被硬编码进 preprocessing 管线native renderer 现在消费的是明确的 chapter render request而不是一组分散选项。
## Files Created/Modified
- [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) - 定义 chapter context、stylesheet package 与 resource diagnostics
- [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) - 实现 preprocessing、CSS 分层、资源解析与 HTML 注入
## Decisions Made
- 明确拒绝把 WebView 分页路径的 `RDEPUBStyleSheetBuilder` 拉回 native renderer。
- EPUB 外链 CSS 保持为 EPUB 层,但通过预处理内联进入统一管线,以确保 user layer 始终处于最后覆盖位。
## Verification
- `rg -n "default|replace|dark|epub|user|stylesheet|preprocess|chapter|baseURL" Sources/RDReaderView/EPUBTextRendering`
- `xcodebuild` simulator build passed via `ReadViewDemo` scheme
## Next Phase Readiness
- Renderer 已经有显式 chapter context 和 stylesheet outputPlan 02 可以直接把 `RDEPUBDTCoreTextRenderer` 接到这个新契约上。
---
*Phase: 02-typesetter-css*
*Completed: 2026-05-22*

View File

@ -1,93 +0,0 @@
---
phase: 02-typesetter-css
plan: 02
type: execute
wave: 2
depends_on:
- "02-01"
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
- Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
- Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
autonomous: true
requirements:
- REND-01
- REND-02
user_setup: []
must_haves:
truths:
- `RDEPUBDTCoreTextRenderer` receives explicit chapter context and stylesheet output.
- `RDEPUBTextBookBuilder` continues to own chapter boundary and page model assembly.
- reflowable EPUB input is no longer just a small builder option bag.
artifacts:
- .planning/phases/02-typesetter-css/02-02-SUMMARY.md
key_links:
- `RDEPUBParser.readingProfile()` still keeps `webFixedLayout` and `webInteractive` out of this path.
- `RDEPUBResourceResolver` remains the authority for baseURL/resource normalization.
---
<objective>
改造 `RDEPUBDTCoreTextRenderer` 与相邻渲染链路,使其承接新的样式分层与章节上下文。
Purpose: 让 native renderer 真正消费 Phase 2.1 产出的 style pipeline并保持章节边界、资源上下文和 fallback 行为稳定。
Output: 对 renderer 和 book builder 的接线调整,使 chapter render path 具备明确的输入契约。
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-typesetter-css/02-RESEARCH.md
@.planning/phases/02-typesetter-css/02-PATTERNS.md
@.planning/phases/02-typesetter-css/02-01-PLAN.md
@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
@Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 接入 chapter context 并简化 renderer 输入</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift</read_first>
<action>把 chapter 级上下文显式传入 renderer包括 `href`、`baseURL`、样式层输出、以及后续资源解析所需的最小元数据。`RDEPUBTextBookBuilder` 继续负责拼接章节、构造 `RDEPUBTextChapter` / `RDEPUBTextPage`,但不再只传一个“扁平 style”给 renderer。`RDEPUBDTCoreTextRenderer` 应该从“DTCoreText option bag 的薄包装”转成“章节 typesetter 的统一入口”。</action>
<verify>rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder|DTHTMLAttributedStringBuilder" Sources/RDReaderView/EPUBTextRendering</verify>
<acceptance_criteria>
- renderer 入参能表达 chapter 级上下文
- renderer 仍保留 fallback path
- book builder 仍然是 page model 的唯一构造点
</acceptance_criteria>
<done>native renderer 的输入契约足够清楚,后续分页语义重构可以直接接在它后面。</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
- [ ] `rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder" Sources/RDReaderView/EPUBTextRendering`
- [ ] 文档与 Phase 2.1 计划不冲突,并且没有把 WebView 路径拉回 native renderer
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- renderer 与 book builder 的职责边界足够稳定Phase 2.3 可以直接用真实样本验证资源解析
</success_criteria>
<output>
After completion, create `.planning/phases/02-typesetter-css/02-02-SUMMARY.md`
</output>

View File

@ -1,70 +0,0 @@
---
phase: 02-typesetter-css
plan: 02
subsystem: epub-text-rendering
tags: [epub, dtcoretext, renderer, chapter-context]
requires: [02-01]
provides:
- "renderer 接收显式 chapter context"
- "book builder 到 renderer 的稳定输入契约"
- "resource diagnostics 沿渲染链路透传"
affects: [phase-02, phase-03, reflowable-engine]
tech-stack:
added: []
patterns: ["chapter-scoped renderer contract", "book-builder-owned pagination boundary"]
key-files:
created: []
modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
key-decisions:
- "`RDEPUBTextBookBuilder` 继续是 chapter boundary 与 page model assembly 的唯一持有者。"
- "`RDEPUBResourceResolver` 扩展为 chapter-relative 资源标准化的权威入口。"
patterns-established:
- "renderer 从 request.context 读取 href/baseURL/stylesheet/resource diagnostics。"
requirements-completed: []
duration: 20min
completed: 2026-05-22
---
# Phase 02 Plan 02: Renderer Wiring Summary
**把新的 chapter stylesheet / preprocessing pipeline 正式接入 `RDEPUBDTCoreTextRenderer``RDEPUBTextBookBuilder`。**
## Performance
- **Duration:** 20 min
- **Completed:** 2026-05-22
- **Tasks:** 1
- **Files modified:** 3
## Accomplishments
- [RDEPUBDTCoreTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift) 现在首先消费 `RDEPUBTextChapterRenderRequest`,再根据 request 中的 `baseURL` 与 style 生成 DTCoreText 输入。
- [RDEPUBTextBookBuilder.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift) 在 chapter boundary 处组装 render request并保留分页、chapter/page model 组装职责不变。
- [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) 新增 chapter-relative `normalizedHref` / `fileURL(forReference:relativeToHref:)`,让资源标准化继续集中在 resolver 层。
## Files Created/Modified
- [RDEPUBDTCoreTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift) - renderer 改为 chapter-scoped 输入契约
- [RDEPUBTextBookBuilder.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift) - builder 负责组装 request并保留 page model 边界
- [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) - chapter-relative 资源标准化与文件定位
## Decisions Made
- 保留 `renderChapter(html:baseURL:style:)` 兼容入口,但统一转发到新的 request-based renderer 契约。
- 资源诊断跟随 render result 一起回到 builder便于后续 demo/sample validation 使用真实 chapter 数据做 spot-check。
## Verification
- `rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder" Sources/RDReaderView/EPUBTextRendering`
- `xcodebuild` simulator build passed via `ReadViewDemo` scheme
## Next Phase Readiness
- 真实样本验证现在可以直接读取 builder 的 `lastBuildResourceDiagnostics`,无需再创建并行 reader shell。
---
*Phase: 02-typesetter-css*
*Completed: 2026-05-22*

View File

@ -1,93 +0,0 @@
---
phase: 02-typesetter-css
plan: 03
type: execute
wave: 3
depends_on:
- "02-02"
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
- Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
- Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
- ReadViewDemo/ReadViewDemo/ViewController.swift
autonomous: true
requirements:
- REND-01
- REND-02
user_setup: []
must_haves:
truths:
- sample EPUBs with images and linked CSS can still resolve chapter resources through the new native pipeline.
- baseURL and resource normalization are verified against real EPUB files, not just code review.
- the validation path stays on the current demo app and does not require a new reader shell.
artifacts:
- .planning/phases/02-typesetter-css/02-03-SUMMARY.md
key_links:
- `RDEPUBResourceResolver` and `RDEPUBParser` are the source of truth for normalized chapter/resource paths.
- sample books in `ReadViewDemo/ReadViewDemo/book/` are the regression corpus.
---
<objective>
验证章节级图片/CSS/基础资源在新渲染输入下可正常解析。
Purpose: 证明 Phase 2.1 和 Phase 2.2 的接线在真实 EPUB 上没有破坏资源加载和章节样式解析。
Output: sample EPUB spot-checks、验证记录和必要的诊断补充。
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-typesetter-css/02-RESEARCH.md
@.planning/phases/02-typesetter-css/02-PATTERNS.md
@.planning/phases/02-typesetter-css/02-01-PLAN.md
@.planning/phases/02-typesetter-css/02-02-PLAN.md
@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
@Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@ReadViewDemo/ReadViewDemo/ViewController.swift
@ReadViewDemo/ReadViewDemo/book/
</context>
<tasks>
<task type="auto">
<name>Task 1: 用真实样本书验证 chapter resource resolution</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift, Sources/RDReaderView/EPUBCore/RDEPUBParser.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</read_first>
<action>使用 `ReadViewDemo/ReadViewDemo/book/` 下的 EPUB 作为验证集,确认新 pipeline 下章节 baseURL、相对 CSS、图片和其他资源仍能正确解析。必要时增加最小的诊断输出或 debug hook但不要引入新的 reader shell。验证重点是“资源真的能被定位和加载”不是只看渲染结果是否大致正常。</action>
<verify>rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo</verify>
<acceptance_criteria>
- sample EPUB 资源加载没有断裂
- relative href / baseURL 解析保持稳定
- demo app 仍然是验证入口
</acceptance_criteria>
<done>Phase 2 的 stylesheet / preprocessing 改造可以被真实书籍证实没有把资源寻址弄坏。</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
- [ ] `rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering`
- [ ] demo app 仍然只依赖现有 reader 主流程,没有新增并行引擎
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- 至少一套 sample EPUB 能证明章节资源和样式层在 native renderer 中是可工作的
</success_criteria>
<output>
After completion, create `.planning/phases/02-typesetter-css/02-03-SUMMARY.md`
</output>

View File

@ -1,67 +0,0 @@
---
phase: 02-typesetter-css
plan: 03
subsystem: demo-validation
tags: [epub, resources, validation, demo]
requires: [02-02]
provides:
- "真实 EPUB 样本资源验证"
- "demo 首页中的验证状态展示"
- "运行时日志里的验证结果"
affects: [phase-02, phase-05, readviewdemo]
tech-stack:
added: []
patterns: ["sample-corpus validation", "demo-surfaced diagnostics"]
key-files:
created: []
modified:
- ReadViewDemo/ReadViewDemo/ViewController.swift
key-decisions:
- "继续使用现有 demo app 作为验证入口,不新增 reader shell。"
- "以 `RDEPUBTextBookBuilder.lastBuildResourceDiagnostics` 为资源完整性判定依据。"
patterns-established:
- "demo 启动时后台扫描 EPUB corpus并把 pass/fail 汇总写到状态标签与运行时日志。"
requirements-completed: []
duration: 20min
completed: 2026-05-22
---
# Phase 02 Plan 03: Sample EPUB Validation Summary
**用 demo 中的真实 EPUB 样本验证了新 native pipeline 的章节资源定位,并把结果直接暴露给 demo UI 与 runtime log。**
## Performance
- **Duration:** 20 min
- **Completed:** 2026-05-22
- **Tasks:** 1
- **Files modified:** 1
## Accomplishments
- 在 [ViewController.swift](/Users/shen/Work/Code/ReadViewSDK/ReadViewDemo/ReadViewDemo/ViewController.swift) 中增加后台 EPUB validation pass自动解析 demo corpus、构建 `RDEPUBTextBook`,并检查 `lastBuildResourceDiagnostics` 是否存在缺失资源。
- 状态标签现在会显示 `EPUB 资源验证X/Y 通过`,运行时日志也会输出同样的汇总,便于 simulator 回归。
- simulator 运行日志记录了:`[ReadViewDemo] EPUB 资源验证2/2 通过`,证明两本 `.textReflowable` 样本书在新 pipeline 下资源解析通过。
## Files Created/Modified
- [ViewController.swift](/Users/shen/Work/Code/ReadViewSDK/ReadViewDemo/ReadViewDemo/ViewController.swift) - 样本校验、UI 文案展示、runtime log 输出
## Decisions Made
- 只把 `.textReflowable` 样本纳入这轮自动验证;`webFixedLayout` / `webInteractive` 仍由原有 WebView 路径负责。
- 验证关注点限定为“资源是否被正确定位并进入渲染链路”,不在本阶段引入新的视觉比对基础设施。
## Verification
- `test -d ReadViewDemo/ReadViewDemo/book`
- `rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo`
- simulator runtime log: `[ReadViewDemo] EPUB 资源验证2/2 通过`
## Next Phase Readiness
- Phase 3 可以在这个资源稳定输入层之上直接推进 attributed string 元数据和复杂分页器,不需要再回头补资源寻址契约。
---
*Phase: 02-typesetter-css*
*Completed: 2026-05-22*

View File

@ -1,32 +0,0 @@
# Phase 2: 重构 typesetter 与 CSS 分层 - Pattern Map
## Goal
为本阶段执行提供“先看哪里、按什么证据写代码”的最短路径。Phase 2 的核心不是分页,而是把章节 HTML、baseURL、资源解析和 CSS 层级变成明确的 native renderer 输入。
## Planned Outputs
| Planned file | Role | Primary evidence | Why this is the right analog |
|--------------|------|------------------|------------------------------|
| `.planning/phases/02-typesetter-css/02-01-PLAN.md` | 章节样式层与 HTML 预处理实现计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | 这些文件已经是 native renderer 的真实入口,最适合承接 stylesheet pipeline。 |
| `.planning/phases/02-typesetter-css/02-02-PLAN.md` | renderer 与 chapter context 接线计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | 这三个文件共同构成 HTML 导入、章节边界和富文本输出链路。 |
| `.planning/phases/02-typesetter-css/02-03-PLAN.md` | 资源解析与样本书验证计划 | `Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`, `ReadViewDemo/ReadViewDemo/book/*.epub` | 资源解析必须靠真实 EPUB 资源和 chapter baseURL 证实,不宜只看代码。 |
## Code Evidence Map
| Concern | Closest source of truth | Evidence to extract |
|---------|-------------------------|---------------------|
| 章节输入如何进入 native renderer | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | chapter `href`、`baseURL`、`renderChapter(...)` 入参、页模型构造点。 |
| 当前 renderer 输入到底有多薄 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` | `DTHTMLAttributedStringBuilder`、`dtOptions(baseURL:style:)`、fallback path。 |
| 现有 HTML 预处理做了什么 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift` | fragment marker 注入、reading attribute normalization、fallback attributed string。 |
| 用户样式与主题从哪里来 | `Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift`, `Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift` | font size / line spacing / theme colors 如何进入 renderer 输入。 |
| 哪些 CSS helper 不能直接复用 | `Sources/RDReaderView/EPUBCore/RDEPUBStyleSheetBuilder.swift` | 这是 WebView 量测/分页 helper不是 native DTCoreText stylesheet pipeline。 |
| 资源如何定位 | `Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift` | 相对路径、manifest item、OPF directory、normalized href。 |
| 哪些样本书最有代表性 | `ReadViewDemo/ReadViewDemo/book/*.epub` | 含图片、复杂段落、外链 CSS 的样本最适合验证 chapter-scoped styles。 |
## Writing Guidance
- 计划文档先把新的 native stylesheet pipeline 命名清楚,再把它挂到现有 `RDEPUBDTCoreTextRenderer``RDEPUBTextBookBuilder` 上。
- 任何涉及样式 precedence 的描述,都要写出 `default / replace / dark / epub / user` 的顺序,而不是只说“增加 CSS 支持”。
- 资源验证优先写 chapter baseURL 和相对资源引用,不要只验证纯文本章节。
- 如果要提到 WebView helper只能作为“不要复用”的反例。

View File

@ -1,177 +0,0 @@
# Phase 2: 重构 typesetter 与 CSS 分层 - Research
**Researched:** 2026-05-21
**Domain:** iOS EPUB reader architecture / native typesetting and chapter-scoped CSS layering
**Confidence:** HIGH
<user_constraints>
## User Constraints (from CONTEXT.md)
No user constraints - all decisions at the agent's discretion.
</user_constraints>
<architectural_responsibility_map>
## Architectural Responsibility Map
| Capability | Primary Tier | Secondary Tier | Rationale |
|------------|-------------|----------------|-----------|
| 章节 HTML 预处理与 CSS 层级组装 | Browser/Client | EPUBTextRendering | 这是 Phase 2 的主战场,必须在 native reflowable 路径里完成。 |
| 章节级 baseURL、资源寻址与样式注入 | Browser/Client | EPUBCore | `RDEPUBTextBookBuilder` 负责提供 chapter boundary 和 baseURL`RDEPUBResourceResolver` 负责资源定位。 |
| 章节 HTML to `NSAttributedString` 导入 | Browser/Client | EPUBTextRendering | `RDEPUBDTCoreTextRenderer` 仍是 DTCoreText 入口,但不能再只依赖一个扁平 option bag。 |
| 主题、字号、行高、背景色等用户输入 | EPUBUI | Browser/Client | `RDEPUBReaderConfiguration` / `RDEPUBPreferences` 仍是用户态入口,但它们必须被转换成可叠加的样式层。 |
| WebView 渲染与测量辅助 | Browser/Client | EPUBCore | `RDEPUBStyleSheetBuilder` 只服务 `WKWebView` 路径,不应直接搬到 native renderer 里。 |
</architectural_responsibility_map>
<research_summary>
## Summary
当前 native reflowable 路径已经有章节边界,但样式输入仍然太薄。`RDEPUBTextBookBuilder` 只负责取出 spine HTML、给出 chapter baseURL然后把原始 HTML 直接交给 `RDEPUBDTCoreTextRenderer`。后者再用 `DTHTMLAttributedStringBuilder` 加一个小型 option bag 导入,最后靠 `normalizeReadingAttributes` 做字体和行距修正。这种结构能跑通基础排版,但不具备 WXRead 风格的显式 CSS 分层也没有把章节资源、publication 级样式和用户样式拆成可验证的输入层。
Phase 2 的正确切入点不是重写 parser也不是把 `RDEPUBStyleSheetBuilder` 从 WebView 路径直接复用到 native 路径。更稳妥的做法是:在 `EPUBTextRendering` 内部建立一个明确的 stylesheet / preprocessing pipeline先把章节 HTML 规范化、补齐 baseURL 和资源引用上下文,再按 `default / replace / dark / epub / user` 的顺序合成样式层,最后把合成后的 HTML/CSS 交给 DTCoreText。
这个阶段的目标是把 renderer 输入从“`DTCoreText` 默认 builder + 少量 options”升级成“章节上下文 + 样式层 + 资源上下文 + 规范化 HTML”。这样后续 Phase 3 才能在不重做入口的前提下,引入更重的页面级属性和分页语义。
**Primary recommendation:** Phase 2 应先把 native stylesheet pipeline 和 chapter preprocessing 固化在 `EPUBTextRendering`,然后再把 `RDEPUBDTCoreTextRenderer` / `RDEPUBTextBookBuilder` 接到这个 pipeline 上,保持 `RDReaderView``WKWebView` 路径不动。
</research_summary>
<standard_stack>
## Standard Stack
### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| UIKit | iOS 15+ SDK | Reader UI and style input | 当前 reader 配置、主题与视图层仍然由 UIKit 提供。 |
| Foundation | System | HTML normalization, URL handling, string processing | 章节级 baseURL、样式拼接与资源路径修正都依赖它。 |
| CoreText | System | Pagination fallback and text measurement | Native book builder 仍需要它做后续分页验证。 |
| DTCoreText | 1.6.28 | HTML/CSS to `NSAttributedString` | Phase 2 的目标是把它从“简单导入器”提升成“可配置的章节 typesetter”。 |
| WebKit | System | Fixed-layout / interactive EPUB only | 只作为对照边界存在,不进入 Phase 2 的 native 分层。 |
### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| ZIPFoundation | 0.9.20 | EPUB archive/resource access | 资源是否能被 chapter baseURL 正确解析,需要它提供底层文件。 |
| DTFoundation | 1.7.19 | DTCoreText support | DTCoreText 解析 HTML / 附件 / 图片资源仍依赖它。 |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Explicit native CSS layering | Continue passing a small `dtOptions` bag | 不能表达 `default / replace / dark / epub / user` 的层级关系,也难验证覆盖顺序。 |
| Chapter-scoped preprocessing in `EPUBTextRendering` | Reuse `RDEPUBStyleSheetBuilder` from WebView path | 会把 WebView 测量语义带进 native renderer职责混淆。 |
| DTCoreText-based native evolution | Replacing DTCoreText with a custom HTML parser | 成本和风险都过高,会偏离当前 roadmap 的“直接重构旧引擎”原则。 |
**Installation:**
```bash
pod install
```
</standard_stack>
<architecture_patterns>
## Architecture Patterns
### System Architecture Diagram
```text
RDEPUBReaderConfiguration / RDEPUBPreferences
-> RDEPUBTextRenderStyle
-> RDEPUBTextBookBuilder
-> chapter HTML + baseURL + resource context
-> native stylesheet / preprocessing pipeline
-> RDEPUBDTCoreTextRenderer
-> DTHTMLAttributedStringBuilder
-> NSAttributedString
-> RDEPUBTextBook / RDEPUBTextPage
```
### Recommended Project Structure
```text
Sources/RDReaderView/
├── EPUBCore/ # Parser, publication, resource resolution, web pagination
├── EPUBTextRendering/ # Native reflowable rendering, preprocessing, pagination
├── EPUBUI/ # Reader configuration, theme, controller wiring
└── RDReaderView.swift # Stable page container and gestures
```
### Pattern 1: Keep chapter context explicit
**What:** Every chapter render should know its `href`, `baseURL`, publication-scoped styles, and user style inputs before DTCoreText import begins.
**When to use:** When a renderer must resolve relative CSS/image/font URLs reliably.
**Example:** `RDEPUBTextBookBuilder` already knows chapter `href` and `baseURL`; Phase 2 should make that context first-class instead of implicit.
### Pattern 2: Make CSS precedence explicit
**What:** Build a deterministic layer order: `default -> replace -> dark -> epub -> user`.
**When to use:** When theme, publication, and user preferences can all change the same visual property.
**Example:** Dark theme text/background colors should be the highest-priority visible override, while EPUB resources remain below user settings.
### Pattern 3: Separate preprocessing from pagination
**What:** Normalize HTML and resolve resources before chapter import; keep pagination logic in the next stage.
**When to use:** When `RDEPUBDTCoreTextRenderer` must remain responsible for HTML-to-attributed-string conversion, not page slicing.
**Example:** Phase 2 should leave `ss_pageRanges(size:)` untouched and focus only on renderer input quality.
### Anti-Patterns to Avoid
- **Using `RDEPUBStyleSheetBuilder` as the native stylesheet engine:** It is a WebView measurement helper and already encodes column/layout semantics that do not belong in native DTCoreText import.
- **Treating `RDEPUBTextRenderStyle` as the final style model:** It currently carries font, line spacing, and colors, but Phase 2 needs a richer chapter-scoped stylesheet pipeline around it.
- **Pushing CSS layering into `RDReaderView`:** The page container should remain a consumer of rendered pages, not a CSS assembly point.
</architecture_patterns>
<dont_hand_roll>
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| HTML parsing | A brand-new EPUB HTML parser | DTCoreText builder extension points | The repo already depends on DTCoreText for native text rendering. |
| Resource resolution | Ad-hoc string replacement for relative URLs | `RDEPUBResourceResolver` and `baseURL` context | Resource resolution must remain chapter-scoped and deterministic. |
| Theme plumbing | Copying WebView CSS injection logic into native renderer | Reader configuration + native stylesheet layers | Native renderer should own native style semantics. |
| Pagination semantics | Page slicing changes in Phase 2 | Keep current `ss_pageRanges(size:)` boundary | Phase 3 owns the heavy pagination rewrite. |
**Key insight:** Phase 2 should make chapter input richer, not make pagination heavier. The win is in the renderer input contract, not in a new page model yet.
</dont_hand_roll>
<common_pitfalls>
## Common Pitfalls
### Pitfall 1: Reusing the WebView stylesheet helper too early
**What goes wrong:** The native path accidentally inherits WebView-only column and viewport semantics.
**Why it happens:** `RDEPUBStyleSheetBuilder` looks like a ready-made stylesheet utility, but it is purpose-built for `WKWebView`.
**How to avoid:** Build a separate native stylesheet helper under `EPUBTextRendering`.
### Pitfall 2: Letting style precedence stay implicit
**What goes wrong:** Theme, user preferences, and EPUB CSS fight each other with no stable priority.
**Why it happens:** `dtOptions` is too small to represent the full cascade.
**How to avoid:** Make layer ordering an explicit API and document it in the renderer contract.
### Pitfall 3: Breaking resource URLs while normalizing HTML
**What goes wrong:** Images, linked stylesheets, and fonts stop loading once HTML is rewritten.
**Why it happens:** Preprocessing can easily strip or relocate nodes without preserving `baseURL`.
**How to avoid:** Keep chapter `baseURL` and resource resolution visible in the pipeline and verify with sample EPUBs.
</common_pitfalls>
<sota_updates>
## State of the Art (2024-2025)
| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Flat HTML import with a small option bag | Explicit chapter-scoped preprocessing plus layered CSS | Already implied by WXRead reference material | Needed to support publication/user/theme overrides in a predictable order. |
| Theme colors applied after import | Style inputs assembled before or during import | Already visible in current repo boundary | Reduces post-processing ambiguity and makes resource/style verification easier. |
| One-size-fits-all renderer input | Chapter context with separate resource/style concerns | Required by Phase 2 scope | Makes baseURL and CSS resolution testable. |
**New tools/patterns to consider:**
- Chapter-scoped stylesheet assembly: keeps `href`, `baseURL`, and style precedence together.
- Pre-import HTML normalization: a safe place to inject style scaffolding without changing page slicing.
**Deprecated/outdated:**
- “Simple DTCoreText renderer + post-hoc normalization” as a long-term design.
- “Borrow WebView CSS helpers for native rendering” as a direct implementation strategy.
</sota_updates>
<open_questions>
## Open Questions
- Should `default` and `replace` layers be generated from engine code, or should some defaults live in bundled EPUB/native CSS assets?
- Where should publication-scoped CSS be sourced from first: OPF manifest, linked chapter CSS files, or a normalized in-memory layer?
- Do we need a dedicated native context type for chapter render input, or can `RDEPUBTextBookBuilder` carry all inputs through existing method parameters?
- How much of the current `RDEPUBTextRenderStyle` should remain public API versus moving into an internal stylesheet model?
</open_questions>

View File

@ -1,76 +0,0 @@
---
phase: 2
slug: typesetter-css
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-05-21
---
# Phase 2 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | shell assertions + Xcode build smoke + demo sample EPUB spot-checks |
| **Config file** | none — current repo has no dedicated XCTest target for this phase |
| **Quick run command** | `test -f .planning/phases/02-typesetter-css/02-RESEARCH.md && test -f .planning/phases/02-typesetter-css/02-PATTERNS.md` |
| **Full suite command** | `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build` |
| **Estimated runtime** | ~60-90 seconds |
---
## Sampling Rate
- **After every task commit:** Run the matching `rg` assertions for the touched plan artifact and confirm the intended source files still name the new pipeline entry points.
- **After every plan wave:** Run `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build`.
- **Before `$gsd-verify-work`:** Build smoke must be green, the three phase docs must exist, and sample EPUBs must still load in the demo app.
- **Max feedback latency:** 90 seconds
---
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 02-01-01 | 01 | 1 | REND-02 | — | CSS layering helper has explicit `default / replace / dark / epub / user` precedence and chapter preprocessing hooks | source | `rg -n "default|replace|dark|epub|user|baseURL|preprocess|stylesheet" Sources/RDReaderView/EPUBTextRendering` | ❌ W0 | ⬜ pending |
| 02-02-01 | 02 | 2 | REND-01, REND-02 | — | `RDEPUBDTCoreTextRenderer` consumes chapter context instead of a flat option bag, and still preserves `RDEPUBTextBookBuilder` chapter boundaries | source | `rg -n "RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder|chapter context|stylesheet|DTHTMLAttributedStringBuilder" Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBUI` | ❌ W0 | ⬜ pending |
| 02-03-01 | 03 | 3 | REND-01, REND-02 | — | Sample EPUBs resolve chapter CSS/image/baseURL references under the new pipeline | smoke/manual | `rg -n "resource|baseURL|css|image|stylesheet" .planning/phases/02-typesetter-css/02-03-PLAN.md` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Wave 0 Requirements
- [ ] No new test target required for Phase 2 planning work.
- [ ] Executor must confirm `ReadViewDemo/ReadViewDemo.xcworkspace` and `ReadViewDemo` scheme still open/build before closing the phase.
- [ ] Sample books in `ReadViewDemo/ReadViewDemo/book/` remain the regression corpus for manual spot-checks.
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| CSS layer precedence is correct in actual rendered chapters | REND-02 | Must compare visible output, not just source strings | Open a sample reflowable EPUB and confirm theme/user/publication styles apply in the intended order. |
| Relative CSS/image/baseURL resolution is intact | REND-01, REND-02 | Resource loading failures are easiest to see in the demo app | Open EPUBs with images and linked CSS and verify they still render after preprocessing. |
| WebView-only style helpers were not repurposed | REND-01, REND-02 | This is a boundary judgement, not a unit assertion | Inspect the final renderer wiring and confirm `RDEPUBStyleSheetBuilder` remains on the WebView path only. |
---
## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references
- [ ] No watch-mode flags
- [ ] Feedback latency < 90s
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending

View File

@ -1,70 +0,0 @@
---
phase: 02-typesetter-css
verified: 2026-05-22T04:14:30Z
status: passed
score: 3/3 must-haves verified
---
# Phase 02: Typesetter CSS Verification Report
**Phase Goal:** 在旧引擎 native reflowable 路径中建立 WXRead 风格的 chapter preprocessing / CSS 分层输入,并验证真实 EPUB 资源解析没有断裂。
**Verified:** 2026-05-22T04:14:30Z
**Status:** passed
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | reflowable renderer 的章节输入不再只是 DTCoreText option bag | ✓ VERIFIED | [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) 新增 `RDEPUBTextChapterContext` / `RDEPUBTextChapterRenderRequest``RDEPUBTextBookBuilder` 在 chapter boundary 组装 request。 |
| 2 | CSS 五层顺序 `default / replace / dark / epub / user` 在 native path 中显式落地 | ✓ VERIFIED | [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) 的 `makeStyleSheetLayers` 固定了分层顺序,并通过 `injectStyleTag` 注入到 chapter HTML。 |
| 3 | 章节级 baseURL、linked CSS、图片资源在真实样本验证中可解析 | ✓ VERIFIED | [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) 扩展了 chapter-relative 标准化demo runtime log 记录 `[ReadViewDemo] EPUB 资源验证2/2 通过`。 |
**Score:** 3/3 truths verified
### Required Artifacts
| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `.planning/phases/02-typesetter-css/02-01-SUMMARY.md` | preprocessing / stylesheet layering 执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 native stylesheet pipeline 与关键决策。 |
| `.planning/phases/02-typesetter-css/02-02-SUMMARY.md` | renderer wiring 执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 request-based renderer contract 与 resolver 变更。 |
| `.planning/phases/02-typesetter-css/02-03-SUMMARY.md` | sample EPUB validation 总结 | ✓ EXISTS + SUBSTANTIVE | 记录 demo corpus 验证与运行时结果。 |
**Artifacts:** 3/3 verified
### Key Link Verification
| From | To | Via | Status | Details |
|------|----|----|--------|---------|
| `RDEPUBTextBookBuilder` | `RDEPUBDTCoreTextRenderer` | chapter render request | ✓ WIRED | builder 组装 `RDEPUBTextChapterRenderRequest` 并透传给 renderer。 |
| stylesheet preprocessing | resource normalization | `RDEPUBResourceResolver` | ✓ WIRED | linked CSS 与 image diagnostics 使用 chapter-relative resolver API。 |
| demo validation | real sample corpus | `ReadViewDemo/ReadViewDemo/book/` | ✓ WIRED | demo 启动时后台扫描 EPUB corpus并输出 `2/2` 通过结果。 |
**Wiring:** 3/3 connections verified
## Requirements Coverage
| Requirement | Status | Notes |
|-------------|--------|-------|
| REND-02 | ✓ SATISFIED | CSS 五层分层已在 native renderer 入口落地。 |
| REND-01 | ◐ PARTIAL | 本阶段完成 renderer 输入与资源语义重构;页面级元数据与复杂分页能力留给 Phase 3。 |
## Human Verification Required
- 若需要视觉确认具体某本书的版式细节,仍建议在 simulator 中手工打开对应 EPUB 做 spot-check。
## Gaps Summary
**No blocking gaps found.** Phase 2 goal achieved. Ready for Phase 3 planning.
## Verification Metadata
**Verification approach:** code inspection + simulator build/run + sample corpus log verification
**Automated checks:** `rg` contract checks, simulator build, demo runtime validation summary
**Human checks required:** optional visual spot-check only
**Total verification time:** ~8 min
---
*Verified: 2026-05-22T04:14:30Z*
*Verifier: inline executor*

View File

@ -1,105 +0,0 @@
---
phase: 03-page-metadata-pagination
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
autonomous: true
requirements:
- REND-03
user_setup: []
must_haves:
truths:
- `RDEPUBTextPage` / `RDEPUBTextChapter` gain explicit page metadata hooks for blocks, attachments, and page-edge reasoning.
- `pageStartOffset`, `pageEndOffset`, chapter text continuity, and fragment offsets remain first-class compatibility fields.
- new metadata is serializable or structurally accessible from current native rendering models rather than hidden inside temporary layout locals.
artifacts:
- .planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md
key_links:
- `RDEPUBTextOffsetRangeInfo` remains compatible with absolute chapter offsets.
- `RDEPUBTextBookBuilder` stays the owner of chapter/page assembly.
---
<objective>
定义页面级 attributed string 元数据与自定义属性键,为 Phase 3 的复杂分页器建立稳定数据契约。
Purpose: 先把“页面语义到底长什么样”固定下来,再让 paginator 和验证围绕这套契约扩展,而不是在分页逻辑里临时发明结构。
Output: richer page/chapter metadata models、attribute key definitions以及保持 offset continuity 的序列化/传递路径。
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
@.planning/phases/02-typesetter-css/02-RESEARCH.md
@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
@Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 定义页面元数据与 attributed string 属性键</name>
<files>Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift</files>
<read_first>Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift</read_first>
<action>在现有 native text 模型旁定义 page metadata 契约和 attributed string 语义键,至少覆盖 block boundary、attachment/image semantic、page break reason、future page style hook 这几类能力;把它们放在 `RDEPUBReadingModels.swift` 或与之紧邻的 native rendering 模型里,并保持 `RDEPUBTextOffsetRangeInfo` 继续基于绝对 chapter offsets 工作。不要删除或弱化 `pageStartOffset`、`pageEndOffset`、`fragmentOffsets`、`href` 等现有兼容字段。</action>
<acceptance_criteria>
- `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift` contains new page metadata types or fields for block / attachment / page break semantics
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift` or adjacent rendering models contain explicit native metadata/attribute-key definitions
- `RDEPUBTextOffsetRangeInfo` remains present and still serializes absolute offset ranges
</acceptance_criteria>
<verify>rg -n "metadata|attachment|block|pageBreak|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift</verify>
<done>native text pipeline now has an explicit metadata vocabulary that later pagination and validation steps can consume.</done>
</task>
<task type="auto">
<name>Task 2: 扩展 chapter/page model 以承接新语义但保留 offset continuity</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift</read_first>
<action>`RDEPUBTextChapter``RDEPUBTextPage` 扩展成可承接新 metadata 的模型,同时保留 `RDEPUBTextBook.pageNumber(for:)`、`location(forPageNumber:)`、search/highlight 所依赖的 offset continuity。若需要新增 page-slice carrier例如 layout snapshot 或 page metadata payload它必须最终落回 `RDEPUBTextPage`,不能绕开现有 `RDEPUBTextBook` 契约。</action>
<acceptance_criteria>
- `RDEPUBTextPage` includes explicit metadata payloads or fields beyond bare range/offsets
- `RDEPUBTextBook.pageNumber(for:)` and `location(forPageNumber:)` still use offset-compatible fields
- source code still contains `pageStartOffset`, `pageEndOffset`, and `fragmentOffsets` after the refactor
</acceptance_criteria>
<verify>rg -n "struct RDEPUBTextPage|struct RDEPUBTextChapter|pageStartOffset|pageEndOffset|fragmentOffsets|metadata" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</verify>
<done>the page model is richer, but all current consumers still have a stable offset-based contract to target.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `rg -n "metadata|attachment|block|pageBreak" Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift Sources/RDReaderView/EPUBTextRendering`
- [ ] `rg -n "pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift`
- [ ] no changes route page presentation around `RDEPUBTextBook`
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No compatibility field needed by highlight/search/navigation is removed
- Phase 3.2 can consume the metadata contract without redefining the page model again
</success_criteria>
<output>
After completion, create `.planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md`
</output>

View File

@ -1,20 +0,0 @@
# 03-01 Summary
## Outcome
Wave 1 landed the page metadata contract without breaking the existing offset-based book model.
- Added `RDEPUBTextPageBreakReason`, `RDEPUBTextAttachmentKind`, and `RDEPUBTextPageMetadata` in `RDEPUBReadingModels.swift`
- Added attributed-string keys in `RDEPUBTextRenderer.swift` for block range/index, fragment id, and attachment kind
- Extended `RDEPUBTextPage` with `metadata`
- Extended `RDEPUBTextChapter` with `pageBreakReasons`
- Tagged normalized attributed content in `RDEPUBTextRendererSupport.normalizeReadingAttributes` so later pagination can inspect block and attachment semantics
## Compatibility
- `pageStartOffset`, `pageEndOffset`, `contentRange`, `fragmentOffsets`, and chapter text continuity remain intact
- Existing `RDEPUBTextBook` page lookup and location mapping continue to operate on absolute chapter offsets
## Notes
This wave deliberately extended the current book contract instead of replacing it. That kept highlight/search/navigation consumers stable while creating a place to land richer pagination semantics in Wave 2.

View File

@ -1,106 +0,0 @@
---
phase: 03-page-metadata-pagination
plan: 02
type: execute
wave: 2
depends_on:
- "03-01"
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift
autonomous: true
requirements:
- REND-03
- REND-04
user_setup: []
must_haves:
truths:
- pagination is no longer implemented as only `CTFrameGetVisibleStringRange` range slicing.
- layouter/layout-frame responsibilities live inside `EPUBTextRendering`, not in reader UI classes.
- stronger page edges still resolve to stable `RDEPUBTextPage` offsets and chapter continuity.
artifacts:
- .planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md
key_links:
- `RDEPUBTextPaginationSupport` remains the entry point for native pagination work.
- `RDEPUBTextBookBuilder` still assembles final pages and chapters.
---
<objective>
在旧引擎基础上重构分页器,引入内部 layouter / layout-frame 语义,并把纯 visible-range slicing 升级为更强的页面边界控制。
Purpose: 让 native paginator 能表达 block / image / attachment 的页面边界语义,同时不破坏现有 page model 与 chapter offset continuity。
Output: stronger paginator implementation, internal layouter/frame types, and book-builder wiring onto the new pagination path.
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
@.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 引入 layouter / layout-frame 语义并升级分页入口</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md</read_first>
<action>把当前 `ss_pageRanges(size:)` 的纯 range slicing 入口升级成内部 layouter / layout-frame 驱动的分页路径;新路径至少要能表达 page break reason、block boundary avoidance、image/attachment boundary handling 这三类语义。新增类型应放在 `EPUBTextRendering` 内部,例如 `RDEPUBTextLayouter` / `RDEPUBTextLayoutFrame`,并由 `RDEPUBTextPaginationSupport` 调用,而不是把复杂分页逻辑堆到 UI controller 里。</action>
<acceptance_criteria>
- `Sources/RDReaderView/EPUBTextRendering` contains layouter/frame types or equivalent internal pagination structures
- paginator code contains semantics beyond plain `CTFrameGetVisibleStringRange` slicing
- source tree does not move pagination logic into `RDEPUBReaderController` or `RDEPUBTextContentView`
</acceptance_criteria>
<verify>rg -n "Layouter|LayoutFrame|pageBreak|attachment|avoid|CTFrameGetVisibleStringRange|CTTypesetter" Sources/RDReaderView/EPUBTextRendering</verify>
<done>native pagination now has a real internal layout vocabulary rather than one visible-range helper.</done>
</task>
<task type="auto">
<name>Task 2: 把更强分页结果接回 `RDEPUBTextBookBuilder` 与现有页模型</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift</read_first>
<action>`RDEPUBTextBookBuilder` 消费新分页结果并把 page-level metadata 落到 `RDEPUBTextPage` / `RDEPUBTextChapter` 上,同时保持 `pageStartOffset` / `pageEndOffset`、chapter order、fragmentOffsets 和 search/highlight overlap 逻辑不变。需要时可以在 builder 内转换 layouter output但最终输出仍必须是当前 `RDEPUBTextBook` 契约,而不是新的 UI-only page list。</action>
<acceptance_criteria>
- `RDEPUBTextBookBuilder` constructs pages from the upgraded pagination output rather than raw `ss_pageRanges(size:)` only
- page model assembly still emits `RDEPUBTextBook(chapters:pages:)`
- search/highlight/navigation dependent fields remain present in the built pages
</acceptance_criteria>
<verify>rg -n "RDEPUBTextBook\\(|pageStartOffset|pageEndOffset|fragmentOffsets|metadata|layout" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</verify>
<done>the richer paginator is wired back into the existing native book contract and remains consumable by later reader phases.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- [ ] `rg -n "Layouter|LayoutFrame|pageBreak|attachment|avoid" Sources/RDReaderView/EPUBTextRendering`
- [ ] `rg -n "RDEPUBTextBook\\(|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- paginator semantics are meaningfully stronger than pure visible-range slicing
- output still flows through the existing native text book/page model
</success_criteria>
<output>
After completion, create `.planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md`
</output>

View File

@ -1,21 +0,0 @@
# 03-02 Summary
## Outcome
Wave 2 replaced the range-only pagination path with an internal layouter/frame vocabulary inside `EPUBTextRendering`.
- Added `RDEPUBTextLayouter.swift`
- Added `RDEPUBTextLayoutFrame.swift`
- Updated `RDEPUBTextPaginationSupport.swift` so `ss_pageRanges(size:)` now delegates to `rd_paginatedFrames(size:fragmentOffsets:)`
- Updated `RDEPUBTextBookBuilder` and `RDPlainTextBookBuilder` to build pages from layout frames instead of bare `NSRange` slices
## New Semantics
- Page breaks now carry one of `chapterEnd`, `frameLimit`, `blockBoundary`, or `attachmentBoundary`
- Layout frames capture block-range context, attachment ranges/kinds, trailing fragment id, and page-edge diagnostics
- The layouter still uses CoreText measurement, but page boundaries are adjusted with block/attachment-aware rules before serialization back into `RDEPUBTextPage`
## Compatibility
- Final output is still `RDEPUBTextBook(chapters: pages:)`
- `pageStartOffset` / `pageEndOffset` remain the compatibility key for search, highlights, and later reader reintegration

View File

@ -1,106 +0,0 @@
---
phase: 03-page-metadata-pagination
plan: 03
type: execute
wave: 3
depends_on:
- "03-02"
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
- Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
- ReadViewDemo/ReadViewDemo/ViewController.swift
autonomous: true
requirements:
- REND-03
- REND-04
user_setup: []
must_haves:
truths:
- sample EPUBs with images or large blocks can prove stronger page-edge semantics through real diagnostics, not only visual inspection.
- offset-based highlight/search compatibility survives the stronger paginator.
- the current demo app remains the validation entry point.
artifacts:
- .planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md
key_links:
- `ReadViewDemo/ReadViewDemo/book/` remains the regression corpus.
- `RDEPUBTextContentView` still derives overlaps from `pageStartOffset` / `pageEndOffset`.
---
<objective>
用真实样本验证复杂块元素、图片与分页边界控制在新分页器下可工作,并确认 offset-compatible highlight/search 行为没有被破坏。
Purpose: 证明 Phase 3 的 richer metadata 和 stronger paginator 不只是结构升级,而是能在真实 EPUB 上给出可解释、可回归的分页结果。
Output: demo-visible diagnostics, sample-book spot-check path, and source assertions around metadata + offset compatibility.
</objective>
<execution_context>
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
@$HOME/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
@.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
@.planning/phases/03-page-metadata-pagination/03-02-PLAN.md
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
@ReadViewDemo/ReadViewDemo/ViewController.swift
@ReadViewDemo/ReadViewDemo/book/
</context>
<tasks>
<task type="auto">
<name>Task 1: 对真实 EPUB 暴露分页诊断并验证图片/块元素边界</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</files>
<read_first>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, ReadViewDemo/ReadViewDemo/ViewController.swift, ReadViewDemo/ReadViewDemo/book/</read_first>
<action>基于现有 demo 样本书,为 stronger paginator 增加最小但可观察的分页诊断输出,至少能报告 page metadata / page break reason / image-or-block boundary evidence 中的一部分,并用 `ReadViewDemo/ReadViewDemo/book/` 的 reflowable EPUB 验证复杂块元素和图片没有回退到不可解释的纯 slicing 行为。不要新增并行 reader shell。</action>
<acceptance_criteria>
- demo or runtime logs expose pagination diagnostics tied to the new metadata or page-break semantics
- source tree still uses `ReadViewDemo/ReadViewDemo/ViewController.swift` as the validation entry point
- `ReadViewDemo/ReadViewDemo/book` remains the sample corpus referenced by the validation path
</acceptance_criteria>
<verify>rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo</verify>
<done>sample books can demonstrate that new pagination semantics are active and inspectable.</done>
</task>
<task type="auto">
<name>Task 2: 验证 offset-compatible 高亮/搜索页内映射没有退化</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift</read_first>
<action>确认 stronger paginator 产出的 page metadata 仍能支持 `RDEPUBTextContentView` 的 highlight/search overlap 计算和 `RDEPUBTextOffsetRangeInfo` 的绝对 offset 语义;如果需要,可补充最小的 source-level guard 或 diagnostics但不要把兼容修复扩展成 Phase 4 的 reader reintegration。重点是验证 page-local presentation 依然能从 absolute chapter offsets 正确切出相对范围。</action>
<acceptance_criteria>
- `RDEPUBTextContentView` still computes overlaps from `pageStartOffset` / `pageEndOffset`
- `RDEPUBTextOffsetRangeInfo` remains decodable and tied to absolute chapter offsets
- no code path replaces highlight/search overlap logic with page-local-only identifiers
</acceptance_criteria>
<verify>rg -n "pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo|searchState" Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore</verify>
<done>Phase 3 finishes with stronger pagination semantics and intact offset-based presentation compatibility.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
- [ ] `rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView ReadViewDemo/ReadViewDemo`
- [ ] `rg -n "pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore`
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- sample books prove stronger paginator behavior is both active and inspectable
- offset-based highlight/search presentation compatibility remains intact for Phase 4 handoff
</success_criteria>
<output>
After completion, create `.planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md`
</output>

View File

@ -1,19 +0,0 @@
# 03-03 Summary
## Outcome
Wave 3 exposed real-book pagination diagnostics and confirmed that offset-compatible highlight/search presentation stayed intact.
- Added builder-level `RDEPUBTextChapterPaginationDiagnostic`
- `RDEPUBTextBookBuilder` now records `lastBuildPaginationDiagnostics`
- `ReadViewDemo/ViewController.swift` reports pagination evidence from sample EPUBs in UI/runtime logs
- `RDEPUBTextContentView.swift` still computes highlight/search overlap from absolute `pageStartOffset` / `pageEndOffset`, now via an explicit helper
## Demo Evidence
Runtime log from the simulator:
- `EPUB 资源验证2/2 通过`
- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
This confirms the stronger paginator is active on real EPUB content and not silently falling back to pure visible-range slicing.

View File

@ -1,31 +0,0 @@
# Phase 3: 重构属性体系与复杂分页器 - Pattern Map
## Goal
为 Phase 3 执行提供“先看哪里、怎么保持兼容”的最短路径。这个阶段的关键不是改 reader UI而是扩展 page metadata 并把 paginator 从纯 range slicing 升级为可表达页面语义的内部 layouter。
## Planned Outputs
| Planned file | Role | Primary evidence | Why this is the right analog |
|--------------|------|------------------|------------------------------|
| `.planning/phases/03-page-metadata-pagination/03-01-PLAN.md` | 页面级 attributed string 元数据与属性键实现计划 | `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift` | 这些文件共同定义 page/chapter contract、range info、fragment offsets 与 renderer output。 |
| `.planning/phases/03-page-metadata-pagination/03-02-PLAN.md` | 复杂分页器与 layouter/frame 重构计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`, `RDEPUBTextBookBuilder.swift` | 当前所有分页都在这里Phase 3 必须从这条路径升级,而不是另起 reader shell。 |
| `.planning/phases/03-page-metadata-pagination/03-03-PLAN.md` | 复杂块元素/图片分页验证计划 | `ReadViewDemo/ReadViewDemo/book/*.epub`, `ReadViewDemo/ReadViewDemo/ViewController.swift`, `RDEPUBTextContentView.swift` | 需要真实样本和现有 demo 入口来证明分页语义变强但 offset compatibility 没坏。 |
## Code Evidence Map
| Concern | Closest source of truth | Evidence to extract |
|---------|-------------------------|---------------------|
| 当前 page model 的边界 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | `RDEPUBTextPage` / `RDEPUBTextChapter` 当前只有 offsets、ranges、fragmentOffsets。 |
| 当前 paginator 的真实实现 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift` | `CTFramesetterCreateFrame` + `CTFrameGetVisibleStringRange` 的纯 slicing 逻辑。 |
| 位置/导航如何依赖 offsets | `Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift` | `pageIndex(for:)`、pending navigation、href/progression 到 page 的映射。 |
| 高亮/选区如何依赖 offsets | `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift` | `RDEPUBTextOffsetRangeInfo``pageStartOffset/pageEndOffset` 的 overlap 计算。 |
| 搜索如何依赖 chapter text continuity | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift` | chapter-level attributed string 搜索 + rangeLocation/rangeLength。 |
| 复杂块元素/图片验证入口 | `ReadViewDemo/ReadViewDemo/book/`, `ReadViewDemo/ReadViewDemo/ViewController.swift` | 现有 demo 与样本 corpus 已经是可运行回归入口。 |
## Writing Guidance
- 计划必须把 “metadata first, paginator second, validation third” 的顺序写清楚,避免把所有变更压进一个 plan。
- 任何新分页语义都要同时写出 “增强点” 和 “必须保留的 offset invariant”。
- 如果引入内部 layouter / layout-frame 类型,职责要落在 `EPUBTextRendering` 内部,不要扩散到 `RDEPUBReaderController`
- 验证计划除了 demo/run还要有源代码断言确保 metadata 与 page-edge reason 真正落盘到模型里。

View File

@ -1,144 +0,0 @@
# Phase 3: 重构属性体系与复杂分页器 - Research
**Researched:** 2026-05-22
**Domain:** iOS EPUB native pagination / page metadata / CoreText layout semantics
**Confidence:** HIGH
<user_constraints>
## User Constraints
No `CONTEXT.md` exists for this phase. Planning is based on ROADMAP requirements, current code, Phase 1 strategy, and Phase 2 implementation artifacts only.
</user_constraints>
<architectural_responsibility_map>
## Architectural Responsibility Map
| Capability | Primary Tier | Secondary Tier | Rationale |
|------------|-------------|----------------|-----------|
| Page-level attributed string metadata | EPUBTextRendering | EPUBCore | Metadata must stay adjacent to chapter/page models so offsets, fragments, and attachments remain queryable. |
| Stronger pagination semantics | EPUBTextRendering | CoreText | `ss_pageRanges(size:)` is the only current paginator; Phase 3 must replace or wrap it with richer layout decisions. |
| Offset continuity for location/highlight/search | EPUBTextRendering | EPUBUI | `RDEPUBTextBook`, `RDEPUBTextContentView`, and `RDEPUBReadingSession` all assume stable chapter offsets. |
| Reader state + navigation orchestration | EPUBCore | EPUBUI | `RDEPUBReadingSession` should keep owning navigation/page lookup; it should consume richer page metadata rather than invent a new UI model. |
| Fixed / interactive EPUB rendering | EPUBCore | WebKit | Still out of scope; WebView paths remain unchanged. |
</architectural_responsibility_map>
<research_summary>
## Summary
Phase 2 solved the renderer input problem. The native reflowable path now has chapter-scoped preprocessing, explicit CSS precedence, and resolver-backed resource diagnostics. What remains weak is the page model and paginator. `RDEPUBTextPaginationSupport.ss_pageRanges(size:)` still slices the chapter with `CTFrameGetVisibleStringRange` and returns only bare `NSRange` values. `RDEPUBTextPage` then stores those ranges plus `pageStartOffset` / `pageEndOffset`, but no block metadata, no attachment semantics, no page-edge reasons, and no way to explain why a page boundary landed where it did.
That limitation is now the main blocker for `REND-03` and `REND-04`. Search, selection, highlights, `RDEPUBReadingSession.pageIndex(for:)`, and `RDEPUBTextContentView` all depend on stable offsets. Phase 3 therefore cannot replace `RDEPUBTextBook` with a new UI-only model. It has to extend the existing `RDEPUBTextChapter` / `RDEPUBTextPage` contract with richer metadata while preserving chapter text continuity and global offsets.
The safest implementation path is incremental:
1. Define page metadata and attribute keys first.
2. Introduce an internal layouter / layout-frame abstraction that can reason about blocks, attachments, and page edges while still returning offset-stable page slices.
3. Validate the new paginator on real EPUBs with large blocks and images before touching Phase 4 reader reintegration.
The repo already exposes the exact compatibility surface we need to preserve:
- `RDEPUBTextContentView` derives relative highlight/search ranges from `pageStartOffset` and `pageEndOffset`.
- `RDEPUBTextOffsetRangeInfo` serializes highlight ranges as absolute chapter offsets.
- `RDEPUBTextSearchEngine` indexes chapter-level attributed strings and stores match ranges by chapter offset.
- `RDEPUBReadingSession` still resolves navigation by href + progression and maps that back onto active pages.
**Primary recommendation:** Build a richer internal pagination layer under `EPUBTextRendering` that emits extended `RDEPUBTextPage` metadata while keeping `pageStartOffset`, `pageEndOffset`, `fragmentOffsets`, and chapter text continuity intact. Do not move layout logic into `RDEPUBReaderController` or `RDEPUBTextContentView`.
</research_summary>
<standard_stack>
## Standard Stack
### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| Foundation | System | Metadata models, ranges, serialization, diagnostics | Existing page/highlight/search models are already Foundation-driven. |
| CoreText | System | Low-level line/frame measurement | Needed to upgrade beyond pure visible-range slicing. |
| UIKit | iOS 15+ SDK | Text page presentation and selection mapping | `RDEPUBTextContentView` still presents `NSAttributedString` pages and maps ranges back to offsets. |
| DTCoreText | 1.6.28 | HTML-to-attributed-string import | Phase 3 builds on imported attributed content rather than replacing the typesetter. |
### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| ZIPFoundation | 0.9.20 | EPUB archive access | Real-book validation still depends on sample EPUB extraction. |
| DTFoundation | 1.7.19 | DTCoreText support | Attachment and HTML import remain rooted here. |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Extend `RDEPUBTextPage` / `RDEPUBTextChapter` | Create a brand-new page tree model | Would break current reader/highlight/search callers too early and enlarge Phase 4 scope. |
| Introduce layouter/frame types under `EPUBTextRendering` | Keep growing `ss_pageRanges(size:)` as a single function | Hard to express page-edge reasons, block avoidance rules, and attachment metadata in one range-only helper. |
| Preserve chapter offsets as the compatibility key | Shift to page-local only locations | Would break selection/highlight persistence and make Phase 4 much harder. |
</standard_stack>
<architecture_patterns>
## Architecture Patterns
### Pattern 1: Extend, don't replace, the book contract
**What:** Add metadata to `RDEPUBTextPage` / `RDEPUBTextChapter` while preserving existing offset fields.
**When to use:** When downstream readers already depend on `href + offsets + pages`.
**Why:** Highlights, search matches, and navigation already serialize offsets and chapter hrefs.
### Pattern 2: Separate layouter from page serialization
**What:** Use internal layouter/layout-frame types to compute page boundaries, then convert those results into `RDEPUBTextPage`.
**When to use:** When pagination logic is becoming more complex than plain visible-range slicing.
**Why:** Keeps `RDEPUBTextBookBuilder` as the chapter/page assembly boundary without forcing it to own all layout heuristics inline.
### Pattern 3: Preserve chapter-level continuity as the invariant
**What:** Every new metadata structure should be traceable back to absolute chapter offsets.
**When to use:** For block edges, attachments, fragment anchors, and page break reasons.
**Why:** Current location/highlight/search behavior uses absolute chapter ranges; Phase 3 should enrich that graph, not replace it.
### Anti-Patterns to Avoid
- Rewriting `RDReaderView` or page presentation controls in the name of pagination.
- Introducing metadata that cannot be mapped back to `pageStartOffset` / `pageEndOffset`.
- Letting validation rely only on “looks okay” manual checks without capturing page-edge or attachment evidence.
</architecture_patterns>
<dont_hand_roll>
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Reader reintegration | A new reader shell | Existing `RDEPUBTextBook` / `RDEPUBReadingSession` contract | Phase 4 needs continuity, not a second UI model. |
| Location persistence | Page-local ephemeral IDs only | Absolute chapter offsets + fragment compatibility | Current highlights and search results already use offset-based range info. |
| Validation | Purely visual checks with no metadata evidence | Source assertions plus sample EPUB diagnostics | Complex pagination bugs need explainable metadata, not only screenshots. |
</dont_hand_roll>
<common_pitfalls>
## Common Pitfalls
### Pitfall 1: Richer metadata that doesn't survive page serialization
If metadata lives only in temporary layout structures and never lands on `RDEPUBTextPage`, Phase 4 cannot consume it.
### Pitfall 2: Better page edges but broken offset continuity
If page boundaries improve but `pageStartOffset` / `pageEndOffset` drift from chapter content, highlights and search navigation regress immediately.
### Pitfall 3: Attachment-aware pagination without validation hooks
Image/attachment rules are hard to trust unless the phase leaves behind enough metadata or demo diagnostics to confirm what happened on a given page boundary.
</common_pitfalls>
<sota_updates>
## State of the Art (2024-2025)
| Old Approach | Current Approach | Impact |
|--------------|------------------|--------|
| `CTFrameGetVisibleStringRange` only | Layout pipeline with page-edge semantics and attachment-aware metadata | Needed to express avoid-break rules and richer page reasoning. |
| Range-only page model | Page model with semantic metadata and diagnostics | Needed for later reader reintegration and regression debugging. |
| Paginator hidden inside a helper | Layouter/frame responsibilities separated from book assembly | Improves testability and makes pagination regressions explainable. |
</sota_updates>
<open_questions>
## Open Questions
- Which page metadata belongs directly on `RDEPUBTextPage` versus a nested layout-metadata payload?
- Should attachment/block metadata be encoded as custom `NSAttributedString.Key` values, separate page structs, or both?
- How much of the new layouter/frame API should remain internal to `EPUBTextRendering` versus surfaced through `RDEPUBTextBookBuilder`?
- Which sample EPUBs in `ReadViewDemo/ReadViewDemo/book/` best expose large-block and image boundary cases for repeatable validation?
</open_questions>

View File

@ -1,63 +0,0 @@
---
phase: 3
slug: page-metadata-pagination
status: draft
nyquist_compliant: true
wave_0_complete: false
created: 2026-05-22
---
# Phase 3 — Validation Strategy
> Per-phase validation contract for page metadata and paginator-semantic work.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | shell assertions + Xcode build smoke + demo EPUB diagnostics |
| **Config file** | none — repo still has no dedicated XCTest target for this phase |
| **Quick run command** | `test -f .planning/phases/03-page-metadata-pagination/03-RESEARCH.md && test -f .planning/phases/03-page-metadata-pagination/03-PATTERNS.md` |
| **Full suite command** | `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 17' build` |
| **Estimated runtime** | ~60-90 seconds |
## Sampling Rate
- **After every task commit:** Run the tasks `rg` assertions against touched rendering/pagination files.
- **After every wave:** Run the `ReadViewDemo` simulator build smoke.
- **Before `$gsd-verify-work`:** Demo sample EPUB diagnostics must still pass, and Phase 3 plans/summaries must exist.
- **Max feedback latency:** 90 seconds
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Secure Behavior | Test Type | Automated Command | Status |
|---------|------|------|-------------|-----------------|-----------|-------------------|--------|
| 03-01-01 | 01 | 1 | REND-03 | Page/chapter models expose richer metadata without dropping offset continuity | source | `rg -n "metadata|attachment|block|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore` | ⬜ pending |
| 03-02-01 | 02 | 2 | REND-04 | Paginator no longer relies on pure visible-range slicing and introduces layouter/frame semantics | source | `rg -n "Layouter|LayoutFrame|pageBreak|avoid|attachment|CTTypesetter|CTFramesetter" Sources/RDReaderView/EPUBTextRendering` | ⬜ pending |
| 03-03-01 | 03 | 3 | REND-03, REND-04 | Sample EPUBs and page diagnostics prove large blocks/images keep offset-compatible pagination | smoke/source | `rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView ReadViewDemo/ReadViewDemo` | ⬜ pending |
## Wave 0 Requirements
- [ ] `ReadViewDemo/ReadViewDemo.xcworkspace` and `ReadViewDemo` scheme still build before close-out.
- [ ] Validation continues to use `ReadViewDemo/ReadViewDemo/book/` as the regression corpus.
- [ ] No plan may remove `pageStartOffset`, `pageEndOffset`, or fragment offset compatibility.
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| Large blocks and images break at better boundaries | REND-04 | Visual edge quality still needs a human read-through | Open a sample EPUB with images and long paragraphs; confirm boundaries are improved versus old slicing. |
| Highlight/search navigation still lands on the correct page after repagination | REND-03, REND-04 | Requires end-to-end interaction with existing reader flow | Use the demo reader to navigate to saved ranges or search matches after pagination changes. |
## Validation Sign-Off
- [ ] All tasks have automated verification commands
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers offset-compatibility invariants
- [ ] No watch-mode flags
- [ ] Feedback latency < 90s
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending

View File

@ -1,31 +0,0 @@
# Phase 3 Verification
## Commands
- `pod install` in `ReadViewDemo/`
- `build_sim` for scheme `ReadViewDemo`
- `build_run_sim` for scheme `ReadViewDemo`
- `rg -n "diagnostic|metadata|page break|attachment|image|block|pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo" Sources/RDReaderView ReadViewDemo/ReadViewDemo -S`
- `test -d ReadViewDemo/ReadViewDemo/book`
## Results
- Build succeeded on iOS Simulator
- App launch succeeded on simulator `iPhone 17`
- Sample corpus directory exists: `ReadViewDemo/ReadViewDemo/book`
- Source verification confirms:
- internal layouter/layout-frame types exist under `EPUBTextRendering`
- paginator now emits page-break semantics and attachment/block diagnostics
- `RDEPUBTextContentView` still uses `pageStartOffset` / `pageEndOffset`
- `RDEPUBTextOffsetRangeInfo` remains the absolute-offset persistence format
## Runtime Evidence
From the app runtime log:
- `EPUB 资源验证2/2 通过`
- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
## Residual Risk
- The layouter heuristics are intentionally incremental, not a full WXRead-equivalent paginator. Phase 4 should validate the stronger page model against live reader navigation/highlight/search flows, and Phase 5 should broaden corpus coverage.

View File

@ -1,89 +0,0 @@
---
phase: 04-reader-capabilities
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
- Sources/RDReaderView/RDURLReaderController.swift
autonomous: true
requirements:
- COMP-01
user_setup: []
must_haves:
truths:
- native text pages remain rendered through the existing `RDEPUBReaderController` + `RDReaderView` shell.
- no new page-turn container or parallel native reader shell is introduced.
- `RDEPUBTextBook` remains the native reflowable backend contract.
artifacts:
- .planning/phases/04-reader-capabilities/04-01-SUMMARY.md
key_links:
- `RDEPUBReaderController.paginatePublication`
- `RDEPUBReaderController.pageContentView`
- `RDEPUBReaderController.restoreReadingLocation`
---
<objective>
把 Phase 3 的 native text backend 稳定接回现有 reader 主流程,让 `RDEPUBReaderController``RDEPUBTextBook` 路径的分页、展示、恢复行为更完整,但不修改 `RDReaderView`
Purpose: 让 reader 主流程明确把 native reflowable path 当成正式 backend而不是零散的特殊分支。
Output: reader-controller/native-text integration cleanup with clearer state handoff and no changes to the page-turn container.
</objective>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-reader-capabilities/04-RESEARCH.md
@.planning/phases/04-reader-capabilities/04-PATTERNS.md
@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
@Sources/RDReaderView/RDURLReaderController.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 收敛 native text backend 的分页完成与展示入口</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift</read_first>
<action>整理 `paginatePublication`、`applyTextBook`、`finishPagination`、`currentVisibleLocation` 和 `restoreReadingLocation` 的 native text 行为,让 `RDEPUBTextBook` 路径拥有清晰的完成/恢复入口,必要时补充最小状态桥接,但不要改 `RDReaderView` 的翻页接口,也不要引入新的 reader shell。</action>
<acceptance_criteria>
- `RDEPUBReaderController` remains the single entry point for native text and web reading
- text-book pagination completion and restore logic are explicit and internally coherent
- no code moves page-turn logic into `RDEPUBTextBookBuilder` or `RDEPUBTextContentView`
</acceptance_criteria>
<verify>rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift</verify>
<done>native text pagination is reconnected as a first-class reader backend.</done>
</task>
<task type="auto">
<name>Task 2: 保持 page container 兼容并清理 native text 初始化入口</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/RDURLReaderController.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/RDURLReaderController.swift</read_first>
<action>确认并收敛 `pageContentView(readerView:pageNum:containerView:)`、TXT 入口和 native content view 配置路径,使 `RDReaderView` 继续只消费 UIView 页面内容而不用理解新的 page metadata。若需要暴露更多 page context优先通过现有 content-view/controller 接口完成。</action>
<acceptance_criteria>
- `RDReaderView` does not gain native metadata-specific branching
- `RDEPUBTextContentView` remains the native page presenter
- TXT/native-text initialization path remains compatible with the same reader shell
</acceptance_criteria>
<verify>rg -n "pageContentView|RDEPUBTextContentView|RDURLReaderController|textBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift</verify>
<done>reader presentation stays container-compatible while native text becomes a stable backend.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
- [ ] `rg -n "pageContentView|RDEPUBTextContentView|RDURLReaderController|textBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`
</verification>
<output>
After completion, create `.planning/phases/04-reader-capabilities/04-01-SUMMARY.md`
</output>

View File

@ -1,16 +0,0 @@
# 04-01 Summary
## Outcome
Wave 1 reconnected the native text backend as a first-class reader path inside the existing shell.
- `RDEPUBReaderController` now builds a real native snapshot from `RDEPUBTextBook` instead of leaving `activePages` / `activeChapters` empty
- Native restore and current-location resolution now go through shared controller helpers instead of scattered text-book special cases
- Native page turns update `RDEPUBReadingSession` with page/spine/chapter context, so reader state stays coherent with the visible page
- `RDReaderView` remained unchanged; it still only consumes content views and page counts
## Compatibility
- No new reader shell or page-turn container was introduced
- TXT and EPUB native text flows still use `RDEPUBReaderController` and `RDEPUBTextContentView`
- `RDEPUBTextBook` remains the backend contract for native reflowable pages

View File

@ -1,90 +0,0 @@
---
phase: 04-reader-capabilities
plan: 02
type: execute
wave: 2
depends_on:
- "04-01"
files_modified:
- Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
- Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
- Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
- Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
autonomous: true
requirements:
- COMP-03
user_setup: []
must_haves:
truths:
- native text highlight/search behavior remains grounded in absolute chapter offsets.
- location mapping and page-number lookup stay coherent after the reader integration cleanup.
- no page-local-only persistence identifier replaces `RDEPUBTextOffsetRangeInfo`.
artifacts:
- .planning/phases/04-reader-capabilities/04-02-SUMMARY.md
key_links:
- `RDEPUBReaderController.normalizedTextSelection`
- `RDEPUBReaderController.search`
- `RDEPUBReadingSession.pageIndex(for:)`
---
<objective>
修复并验证 native text reader 下的位置映射、高亮、选区、搜索和 TOC/跳转兼容能力,确保 Phase 3 的 richer paginator 不会破坏这些用户能力。
Purpose: 把 offset-compatible 的底层契约真正接回 reader 行为链路。
Output: source fixes and verification around native location lookup, selection normalization, search navigation, and persisted highlights.
</objective>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-reader-capabilities/04-RESEARCH.md
@.planning/phases/04-reader-capabilities/04-PATTERNS.md
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
@Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 收敛 native text 的 location / selection / highlight 兼容语义</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift</read_first>
<action>验证并修复 native text selection/highlight 链路,重点围绕 `normalizedTextSelection`、`RDEPUBTextOffsetRangeInfo`、`pageStartOffset` / `pageEndOffset` overlap 计算和 reader persistence 语义展开。若要补充 guard 或 normalization必须继续以 chapter href + absolute offsets 为核心,而不是引入 page-local-only id。</action>
<acceptance_criteria>
- `RDEPUBTextOffsetRangeInfo` remains the persisted native text range format
- native selection/highlight restore still maps through absolute chapter offsets
- `RDEPUBTextContentView` still derives visible ranges from page offsets
</acceptance_criteria>
<verify>rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore</verify>
<done>native text highlight and selection persistence remain offset-compatible.</done>
</task>
<task type="auto">
<name>Task 2: 修复并验证 native text 搜索 / TOC / 跳转页号解析</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift</read_first>
<action>核对 native text path 的 `pageNumber(for:)`、`search`、`navigateToCurrentSearchMatch`、TOC 页号解析与内部链接跳转,确保 richer page model 下这些 reader feature 仍能落到正确页。需要时可以增强 lookup/normalization但不要把 Web 路径逻辑硬搬到 native path。</action>
<acceptance_criteria>
- native text search still produces usable page navigation
- TOC/page-number resolution stays coherent for `RDEPUBTextBook`
- reader state and page lookup are not duplicated into a second custom navigation stack
</acceptance_criteria>
<verify>rg -n "search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift</verify>
<done>native text search, TOC, and jumps remain believable after reintegration.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore`
- [ ] `rg -n "search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift`
</verification>
<output>
After completion, create `.planning/phases/04-reader-capabilities/04-02-SUMMARY.md`
</output>

View File

@ -1,15 +0,0 @@
# 04-02 Summary
## Outcome
Wave 2 tightened native location, highlight, selection, search, and TOC compatibility around the Phase 3 offset contract.
- `normalizedTextSelection` now preserves exclusive end semantics more carefully when rebuilding progressions from `RDEPUBTextOffsetRangeInfo`
- Native highlight filtering now compares normalized hrefs, reducing mismatch risk after resolver normalization
- Search page lookup now prefers exact `rangeLocation` offset matching before falling back to progression-based page resolution
## Compatibility
- `RDEPUBTextOffsetRangeInfo` remains the native persisted range format
- `RDEPUBTextContentView` still derives visible overlap from `pageStartOffset` / `pageEndOffset`
- No page-local-only identifier replaced the absolute chapter offset contract

View File

@ -1,90 +0,0 @@
---
phase: 04-reader-capabilities
plan: 03
type: execute
wave: 3
depends_on:
- "04-02"
files_modified:
- Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
- Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
- Sources/RDReaderView/RDURLReaderController.swift
- ReadViewDemo/ReadViewDemo/ViewController.swift
autonomous: true
requirements:
- COMP-04
user_setup: []
must_haves:
truths:
- repagination after font, line-height, theme, or viewport changes restores semantic reading state.
- the demo app remains the primary runtime verification entry point.
- `RDReaderView` does not need modification to validate these flows.
artifacts:
- .planning/phases/04-reader-capabilities/04-03-SUMMARY.md
key_links:
- `RDEPUBReaderController.configuration.didSet`
- `RDEPUBReaderController.handleViewportChangeIfNeeded`
- `RDEPUBReaderController.rebuildExternalTextBook`
---
<objective>
验证并收敛字体、行高、主题和 viewport 变化导致的重新分页与状态恢复,让 reader 在 Phase 4 完成后可以稳定承接 repagination。
Purpose: 证明新 native backend 在真实 reader 交互下能够重新分页而不丢失位置、搜索和高亮上下文。
Output: re-pagination/state-restore fixes, plus demo-visible validation hooks or diagnostics where needed.
</objective>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-reader-capabilities/04-RESEARCH.md
@.planning/phases/04-reader-capabilities/04-PATTERNS.md
@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
@Sources/RDReaderView/RDURLReaderController.swift
@ReadViewDemo/ReadViewDemo/ViewController.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: 收敛 repagination 触发器与状态恢复逻辑</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/RDURLReaderController.swift</files>
<read_first>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/RDURLReaderController.swift</read_first>
<action>围绕 `configuration.didSet`、`requiresRepagination`、`refreshVisibleContentPreservingLocation`、`rebuildExternalTextBook`、`handleViewportChangeIfNeeded` 和 TXT/native 入口收敛 repagination 行为,使 reader 在设置变化与 viewport 变化后恢复到语义位置,而不是错误页或空状态。</action>
<acceptance_criteria>
- font size / line height changes trigger repagination through semantic location restore
- theme changes refresh visible content without dropping current reader state
- viewport changes do not require `RDReaderView` changes to preserve location
</acceptance_criteria>
<verify>rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|configuration" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift Sources/RDReaderView/RDURLReaderController.swift</verify>
<done>repagination triggers are stable and state-preserving.</done>
</task>
<task type="auto">
<name>Task 2: 在 demo 中验证 reader-level repagination 与状态恢复</name>
<files>ReadViewDemo/ReadViewDemo/ViewController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift</files>
<read_first>ReadViewDemo/ReadViewDemo/ViewController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, ReadViewDemo/ReadViewDemo/book/</read_first>
<action>用现有 demo 入口补充最小但可观察的 reader-level 验证路径或日志,证明 Phase 4 关注的 repagination / location restore / search-highlight continuity 可以被检查。不要新建并行 demo shell。</action>
<acceptance_criteria>
- demo remains the validation entry point
- runtime checks can confirm at least part of the repagination/state-restore behavior
- sample corpus under `ReadViewDemo/ReadViewDemo/book/` remains the verification source
</acceptance_criteria>
<verify>test -d ReadViewDemo/ReadViewDemo/book && rg -n "restore|repagination|search|highlight|location|theme|font" ReadViewDemo/ReadViewDemo Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift</verify>
<done>Phase 4 finishes with a real runtime path to prove state-preserving repagination.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|configuration" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift Sources/RDReaderView/RDURLReaderController.swift`
- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
- [ ] `rg -n "restore|repagination|search|highlight|location|theme|font" ReadViewDemo/ReadViewDemo Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
</verification>
<output>
After completion, create `.planning/phases/04-reader-capabilities/04-03-SUMMARY.md`
</output>

View File

@ -1,19 +0,0 @@
# 04-03 Summary
## Outcome
Wave 3 validated repagination and semantic location restore with real sample books.
- The demo now reports a restore diagnostic alongside the pagination diagnostic
- Validation rebuilds native text books with a larger font/line spacing and a theme-only style change, then checks whether the same semantic location still resolves correctly
- Runtime evidence confirmed stable href restoration and theme-stable page mapping on the sampled reflowable EPUB
## Demo Evidence
Runtime log from the simulator:
- `EPUB 资源验证2/2 通过`
- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
- `恢复诊断:宝山辽墓材料与释读 · base 39 · font-shift 44 · theme-stable yes · href-stable yes · progression-delta 0.004`
This shows the native reader backend can repaginate to a different page number under font changes while preserving the same semantic reading location.

View File

@ -1,29 +0,0 @@
# Phase 4: 接回现有 reader 能力链路 - Pattern Map
## Goal
为 Phase 4 提供“先接哪里、怎么验证没把 reader 行为搞坏”的最短路径。重点不是继续改 paginator而是把已经完成的 native text book/page model 稳定接回 reader 能力链路。
## Planned Outputs
| Planned file | Role | Primary evidence | Why this is the right analog |
|--------------|------|------------------|------------------------------|
| `04-01-PLAN.md` | native text backend 接回 reader 主流程 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBTextContentView`, `RDURLReaderController` | 这些文件定义当前 native text 打开、分页、展示和 page container 接口。 |
| `04-02-PLAN.md` | 位置映射 / 高亮 / 搜索兼容收敛 | `RDEPUBReadingSession`, `RDEPUBReaderController`, `RDEPUBTextSearchEngine`, `RDEPUBReadingModels` | 这些文件共同定义 reader state、search matches、selection normalization 与绝对 offset 持久化语义。 |
| `04-03-PLAN.md` | 主题 / 字号 / 行高 / viewport 变化后的重新分页与状态恢复验证 | `RDEPUBReaderController`, `RDEPUBReaderConfiguration`, `RDURLReaderController`, `ReadViewDemo` | 当前所有重新分页触发器和 demo 验证入口都在这里。 |
## Code Evidence Map
| Concern | Closest source of truth | Evidence to extract |
|---------|-------------------------|---------------------|
| native text page flow接入点 | `RDEPUBReaderController.paginatePublication`, `applyTextBook`, `pageContentView` | 现有 controller 已有 native/web 双路径,但需要收敛行为。 |
| 恢复位置与可见位置 | `restoreReadingLocation`, `currentVisibleLocation`, `finishPagination` | 这里决定 repagination 后回到哪里。 |
| 绝对 offset 高亮/选区兼容 | `RDEPUBTextContentView`, `normalizedTextSelection`, `RDEPUBTextOffsetRangeInfo` | 这些是 Phase 4 不能破坏的硬约束。 |
| 搜索跳转与页号解析 | `search`, `navigateToCurrentSearchMatch`, `pageNumber(for:)`, `RDEPUBTextSearchEngine` | 需要保证 native text path 仍能定位正确页。 |
| 设置变更触发重排 | `configuration.didSet`, `requiresRepagination`, `refreshVisibleContentPreservingLocation`, `handleViewportChangeIfNeeded` | 字号/行高/主题/旋转都是 Phase 4 的状态恢复验证入口。 |
## Writing Guidance
- 计划必须把“主流程接回、兼容能力收敛、重排恢复验证”拆成三个独立 plan不要把所有 reader 能力挤进一个任务。
- 任何修复都要优先围绕 `RDEPUBReaderController``RDEPUBReadingSession` 的现有责任边界展开,不要借机重写 `RDReaderView`
- 验证步骤要明确要求真实的 reader 行为检查,而不是只做 build pass。

View File

@ -1,123 +0,0 @@
# Phase 4: 接回现有 reader 能力链路 - Research
**Researched:** 2026-05-22
**Domain:** iOS EPUB reader reintegration / location mapping / highlight-search-state continuity
**Confidence:** HIGH
<user_constraints>
## User Constraints
No `CONTEXT.md` exists for this phase. Planning is based on ROADMAP requirements, current code, and completed artifacts from Phases 1-3 only.
</user_constraints>
<architectural_responsibility_map>
## Architectural Responsibility Map
| Capability | Primary Tier | Secondary Tier | Rationale |
|------------|-------------|----------------|-----------|
| Reflowable native pagination output | EPUBTextRendering | EPUBCore | Phase 3 already moved richer page semantics into `RDEPUBTextBook` / `RDEPUBTextPage`. |
| Reader orchestration and restore logic | EPUBUI | EPUBCore | `RDEPUBReaderController` owns `paginatePublication`, `finishPagination`, restore, search navigation, selection, and configuration updates. |
| Page lookup and navigator state | EPUBCore | EPUBUI | `RDEPUBReadingSession` remains the non-UI state owner for fixed/web flows and should keep that role rather than pushing state into `RDReaderView`. |
| Highlight/search persistence semantics | EPUBCore | EPUBUI | `RDEPUBTextOffsetRangeInfo` and normalized locations are the durable compatibility contract. |
| Page-turn UI and display modes | RDReaderView | EPUBUI | Explicitly out of scope to change; Phase 4 must adapt to existing `RDReaderView` APIs. |
</architectural_responsibility_map>
<research_summary>
## Summary
Phase 3 strengthened the native reflowable engine but did not yet make that richer page model a first-class citizen across the whole reader capability chain. The main Phase 4 work is therefore not “more pagination”; it is reconnecting the existing reader shell to the upgraded `RDEPUBTextBook` path without changing `RDReaderView` or introducing a second native reader model.
The current code already shows the exact reintegration seams:
- `RDEPUBReaderController.paginatePublication` branches between native `RDEPUBTextBookBuilder`, fixed-layout snapshots, and `RDEPUBPaginator` WebKit measurement.
- `applyTextBook`, `finishPagination`, `restoreReadingLocation`, and `currentVisibleLocation` already contain a parallel text-book path that bypasses `RDEPUBReadingSession.pageIndex(for:)`.
- `pageContentView(readerView:pageNum:containerView:)` already swaps between `RDEPUBTextContentView` and `RDEPUBWebContentView`.
- Search uses `RDEPUBTextSearchEngine` when `textBook` exists and `RDEPUBHTMLSearchEngine` otherwise.
- Selection/highlight persistence for native text still depends on `RDEPUBTextOffsetRangeInfo` absolute offsets and chapter href normalization.
That means Phase 4 should stabilize and tighten an already-existing split path rather than invent a new architecture. The right outcome is:
1. Reflowable native pages remain rendered through `RDEPUBTextBook`.
2. `RDEPUBReaderController` keeps one coherent restore/navigation/search/highlight API no matter whether the source page is native text or Web content.
3. Repagination triggered by font size, line height, viewport changes, or theme changes preserves reader state instead of resetting to page 1 or dropping search/highlight context.
The riskiest compatibility areas are also already visible:
- `RDEPUBReaderController.restoreReadingLocation` and `currentVisibleLocation` use separate logic for text-book vs snapshot pages.
- `normalizedTextSelection` recomputes progression from absolute offset payloads; regressions here would silently break persisted highlights.
- `refreshVisibleContentPreservingLocation` and `rebuildExternalTextBook` can preserve location only if the text-book lookup contract remains stable after repagination.
- `flattenedTableOfContentsItems` and `pageNumber(for:)` need page-number continuity for TOC and search to stay believable.
**Primary recommendation:** Treat Phase 4 as a reader-state consolidation phase. Strengthen `RDEPUBReaderController` and adjacent state surfaces so the Phase 3 native pagination model behaves like a first-class reader backend while preserving the existing `RDReaderView` page-turn container and the fixed / interactive WebKit paths.
</research_summary>
<code_evidence>
## Code Evidence
| Concern | Source | Why it matters |
|--------|--------|----------------|
| Native-vs-web pagination entry split | `RDEPUBReaderController.paginatePublication` | Defines Phase 4s main re-integration entry point. |
| Native restore and current-location logic | `RDEPUBReaderController.restoreReadingLocation`, `currentVisibleLocation` | Text-book navigation already bypasses `RDEPUBReadingSession`; this must stay coherent. |
| Reader state owner for non-native flows | `RDEPUBReadingSession` | Existing navigation/session state should remain centralized instead of leaking into UI code. |
| Native selection/highlight normalization | `RDEPUBReaderController.normalizedTextSelection`, `RDEPUBTextContentView`, `RDEPUBReadingModels` | Absolute offsets must survive reader interactions and persistence. |
| Search backend switching | `RDEPUBReaderController.resolvedSearchMatches`, `RDEPUBTextSearchEngine` | Search behavior must remain uniform from the users perspective across render backends. |
| Repagination triggers | `RDEPUBReaderController.configuration.didSet`, `handleViewportChangeIfNeeded`, `rebuildExternalTextBook`, `repaginatePreservingCurrentLocation` | These are the operational triggers most likely to break restore/state continuity. |
</code_evidence>
<architecture_patterns>
## Architecture Patterns
### Pattern 1: Consolidate reader behavior at the controller boundary
**What:** Keep branching localized in `RDEPUBReaderController`, but make navigation, restore, search, and persistence semantics consistent across text-book and web paths.
**Why:** The UI entry point already exists there; moving branching elsewhere would enlarge scope.
### Pattern 2: Keep offsets as the native compatibility key
**What:** Native text selection, search, and highlight flows should continue to resolve through chapter href + absolute offset payloads.
**Why:** Phase 3 deliberately preserved this invariant to make Phase 4 possible.
### Pattern 3: Repaginate by restoring location, not page number
**What:** Font/theme/viewport changes should always preserve a semantic location and rebuild around it.
**Why:** Page numbers are not stable under re-pagination; href + progression + offset-compatible selection are.
### Anti-Patterns to Avoid
- Refactoring `RDReaderView` to understand native page metadata directly.
- Creating a second navigation state machine just for the native text path.
- Switching highlights/search to page-local identifiers that cannot survive repagination.
</architecture_patterns>
<dont_hand_roll>
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Native reader shell | A new dedicated native-text controller | Existing `RDEPUBReaderController` split path | Scope stays contained and existing app entry points remain valid. |
| Restore state | Page-number-only recovery | `RDEPUBLocation` + native page lookup + offset contract | Page numbers drift after repagination. |
| Search compatibility | Separate search UI semantics for text pages | Existing `RDEPUBSearchState` and `RDEPUBSearchMatch` flow | User-facing search behavior should stay unified. |
</dont_hand_roll>
<common_pitfalls>
## Common Pitfalls
### Pitfall 1: Letting native and web reader paths diverge in user-visible behavior
If search, selection, or TOC navigation behaves differently based on backend, the reader becomes unpredictable.
### Pitfall 2: Preserving page numbers instead of semantic locations during repagination
This causes subtle wrong-page restores after font, theme, or viewport changes.
### Pitfall 3: Refreshing visible content without reapplying current search/highlight state
Native text pages are recreated from attributed substrings, so state must be re-derived every time.
</common_pitfalls>
<open_questions>
## Open Questions
- Should any of the new Phase 3 page metadata be surfaced to reader delegates in Phase 4, or remain internal until Phase 5 diagnostics?
- Is `RDEPUBReadingSession` worth extending to cover native `RDEPUBTextBook` page lookup as well, or is the current controller-local split still the smallest safe change?
- Which search/highlight and TOC navigation paths are most important to validate interactively in the demo during execution?
</open_questions>

View File

@ -1,29 +0,0 @@
# Phase 4: 接回现有 reader 能力链路 - Validation
## Required Checks
- `RDEPUBReaderController` 仍是 native text / web reader 的统一入口
- `RDReaderView` 不需要新增 native page metadata 感知逻辑
- native text 的高亮、搜索、TOC、恢复位置仍通过 `RDEPUBLocation` 与绝对 offset 兼容语义工作
- 字号、行高、主题、viewport 变化后的 repagination 不会把阅读状态重置为错误位置
## Verification Anchors
### Source-level
- `rg -n "paginatePublication|applyTextBook|restoreReadingLocation|currentVisibleLocation|finishPagination|pageContentView" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
- `rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|search\\(|pageNumber\\(for: searchMatch\\)" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering`
- `rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|handleViewportChangeIfNeeded|rebuildExternalTextBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift`
### Runtime / demo
- open a reflowable EPUB in `ReadViewDemo`
- navigate via TOC and ensure the target page remains correct on the native text path
- create or load a highlight, then verify it still paints after page refresh / repagination
- run a search and verify current match navigation lands on the right page
- change font size, line height, and theme; confirm the reader restores to the same semantic location
## Completion Standard
Phase 4 planning is only good enough if each executable plan names both:
1. the code seam it will modify
2. the reader behavior it must preserve or prove

View File

@ -1,32 +0,0 @@
# Phase 4 Verification
## Commands
- `build_sim` for scheme `ReadViewDemo`
- `build_run_sim` for scheme `ReadViewDemo`
- `test -d ReadViewDemo/ReadViewDemo/book`
- `rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation|RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:|requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|restore|repagination|highlight|location|theme|font" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo -S`
## Results
- Simulator build succeeded
- Simulator launch succeeded
- Sample corpus directory exists
- Source verification confirms:
- native text pagination now feeds a real reader snapshot and location helpers
- native selection/highlight compatibility still depends on absolute chapter offsets
- native search page lookup now uses exact offset evidence when available
- repagination and restore hooks remain centered in `RDEPUBReaderController`, not `RDReaderView`
## Runtime Evidence
From the app runtime log:
- `EPUB 资源验证2/2 通过`
- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
- `恢复诊断:宝山辽墓材料与释读 · base 39 · font-shift 44 · theme-stable yes · href-stable yes · progression-delta 0.004`
## Residual Risk
- The restore validation is still corpus-level and not a full interactive UI automation pass through TOC/highlight/search actions
- The native and web paths still branch inside `RDEPUBReaderController`; Phase 5 should focus on broader regression and stability coverage rather than further architectural expansion

View File

@ -1,84 +0,0 @@
---
phase: 05-regression-stability
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- ReadViewDemo/ReadViewDemo/ViewController.swift
- .planning/phases/05-regression-stability/05-VALIDATION.md
- .planning/phases/05-regression-stability/05-RESEARCH.md
autonomous: true
requirements:
- STAB-01
user_setup: []
must_haves:
truths:
- Phase 5 needs an explicit sample matrix, not only ad hoc simulator runs.
- demo/runtime diagnostics are the primary validation entry point.
- the matrix must account for native reflowable, fixed/interactive, and TXT-compatible paths.
artifacts:
- .planning/phases/05-regression-stability/05-01-SUMMARY.md
key_links:
- `ReadViewDemo/ReadViewDemo/book/`
- `ReadViewDemo/ReadViewDemo/ViewController.swift`
---
<objective>
构建样本书验证矩阵、诊断输出和复现清单,让 Phase 5 的回归验证有明确覆盖面和可重复证据。
Purpose: 先把“验证什么、用什么样本、看什么证据”固定下来,再进入稳定性收敛。
Output: documented sample matrix plus demo-visible or log-visible validation summary covering the required book categories.
</objective>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-regression-stability/05-RESEARCH.md
@.planning/phases/05-regression-stability/05-PATTERNS.md
@.planning/phases/04-reader-capabilities/04-VERIFICATION.md
@ReadViewDemo/ReadViewDemo/ViewController.swift
@ReadViewDemo/ReadViewDemo/book/
</context>
<tasks>
<task type="auto">
<name>Task 1: 定义样本矩阵与风险映射</name>
<files>.planning/phases/05-regression-stability/05-RESEARCH.md, .planning/phases/05-regression-stability/05-VALIDATION.md, ReadViewDemo/ReadViewDemo/ViewController.swift</files>
<read_first>.planning/phases/05-regression-stability/05-RESEARCH.md, ReadViewDemo/ReadViewDemo/ViewController.swift, ReadViewDemo/ReadViewDemo/book/</read_first>
<action>明确每本样本书对应的验证类别与风险映射,并在需要时把这份矩阵体现在 demo 诊断输出或 phase 文档中。矩阵至少覆盖:小说/长文本 reflowable、复杂块元素/图片 reflowable、fixed 或 interactive WebKit 路径,以及 TXT 路径(如当前缺样本,则需在执行期明确补充策略)。</action>
<acceptance_criteria>
- sample corpus is mapped to explicit regression categories
- Phase 5 artifacts state what evidence each sample is expected to provide
- demo remains the primary visible validation entry point
</acceptance_criteria>
<verify>find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort && rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S</verify>
<done>Phase 5 has an explicit validation matrix instead of an implicit sample list.</done>
</task>
<task type="auto">
<name>Task 2: 增强可重复诊断输出与复现清单</name>
<files>ReadViewDemo/ReadViewDemo/ViewController.swift, .planning/phases/05-regression-stability/05-VALIDATION.md</files>
<read_first>ReadViewDemo/ReadViewDemo/ViewController.swift, .planning/phases/04-reader-capabilities/04-VERIFICATION.md</read_first>
<action>在现有 demo summary 基础上补充最小但可重复的诊断输出或验证说明,使后续 Phase 5 回归结果能被再次运行并比对。不要引入新的测试壳;应复用当前 demo 和已有日志体系。</action>
<acceptance_criteria>
- runtime validation evidence is clearer than a generic success/failure line
- there is a written checklist or summary path for rerunning the same validations
- diagnostics remain lightweight and tied to existing sample books
</acceptance_criteria>
<verify>rg -n "validation|diagnostic|restore|pagination|search|highlight|fixed|resource" ReadViewDemo/ReadViewDemo/ViewController.swift .planning/phases/05-regression-stability/05-VALIDATION.md</verify>
<done>Phase 5 starts with a repeatable evidence-producing validation layer.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
- [ ] `rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S`
</verification>
<output>
After completion, create `.planning/phases/05-regression-stability/05-01-SUMMARY.md`
</output>

View File

@ -1,21 +0,0 @@
# 05-01 Summary
## Outcome
Wave 1 turned the sample corpus into an explicit regression matrix and made the demo startup log the primary repeatable proof surface.
- `ViewController` now validates every local `.epub` and `.txt` sample instead of only native reflowable EPUBs
- startup summary now groups books into `TXT/小说`、`复杂图文`、`Fixed/互动`、`TXT` categories
- a built-in TXT sample, `ReadViewDemo/ReadViewDemo/book/回归验证样本.txt`, now keeps the TXT path inside the repo instead of relying on an external file
## Evidence
Runtime log after simulator launch:
- `样本验证5/5 通过`
- `矩阵[复杂图文] 2/2 · 宝山辽墓材料与释读(textReflowable)、张学良传(textReflowable)`
- `矩阵[Fixed/互动] 2/2 · 《凡人修仙传》精校版全本(webInteractive)、爱忘事的熊爷爷(webFixedLayout)`
- `矩阵[TXT] 1/1 · 回归验证样本(txt)`
The demo remains the only validation surface; no extra test shell was introduced.

View File

@ -1,101 +0,0 @@
---
phase: 05-regression-stability
plan: 02
type: execute
wave: 2
depends_on:
- "05-01"
files_modified:
- Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
- Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift
- Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift
- Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift
- Sources/RDReaderView/RDURLReaderController.swift
- ReadViewDemo/ReadViewDemo/ViewController.swift
autonomous: true
requirements:
- STAB-02
user_setup: []
must_haves:
truths:
- Phase 5 is allowed to fix concrete regressions discovered by the validation matrix.
- fixes should be evidence-driven and narrow in scope.
- no new architecture should be introduced in a convergence phase.
artifacts:
- .planning/phases/05-regression-stability/05-02-SUMMARY.md
- .planning/phases/05-regression-stability/05-VERIFICATION.md
key_links:
- `RDEPUBReaderController`
- `RDEPUBTextBookBuilder`
- `RDEPUBWebView*`
- `RDURLReaderController`
---
<objective>
依据样本矩阵执行回归,定位并收敛分页、位置映射、图片/块元素分页、fixed/interactive 资源加载,以及主题切换后的稳定性问题。
Purpose: 用真实样本和 reader 主流程把 Phase 1-4 的改动压测到可交付稳定水平。
Output: regression fixes where needed, plus a verification artifact showing which main flows and sample categories passed.
</objective>
<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-regression-stability/05-RESEARCH.md
@.planning/phases/05-regression-stability/05-PATTERNS.md
@.planning/phases/05-regression-stability/05-VALIDATION.md
@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
@.planning/phases/04-reader-capabilities/04-VERIFICATION.md
@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
@Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift
@Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift
@Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift
@Sources/RDReaderView/RDURLReaderController.swift
@ReadViewDemo/ReadViewDemo/ViewController.swift
@ReadViewDemo/ReadViewDemo/book/
</context>
<tasks>
<task type="auto">
<name>Task 1: 按矩阵执行 reader 主流程回归并修复具体问题</name>
<files>Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/RDURLReaderController.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</files>
<read_first>.planning/phases/05-regression-stability/05-VALIDATION.md, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/RDURLReaderController.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</read_first>
<action>基于样本矩阵执行 native reflowable 与 TXT 相关 reader 主流程验证包括打开、位置恢复、搜索、highlight、TOC、theme/font repagination并对发现的具体问题做最小修复。重点是收敛错误页、状态丢失、刷新后高亮/搜索不一致、TXT 入口回退等问题。</action>
<acceptance_criteria>
- key native/TXT reader flows have explicit pass evidence
- any fixes are traceable to a concrete regression found during validation
- no broad architectural refactor is introduced
</acceptance_criteria>
<verify>rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift</verify>
<done>native and TXT reader flows are regression-checked and stabilized.</done>
</task>
<task type="auto">
<name>Task 2: 回归 fixed / interactive WebKit 路径并收敛资源加载与展示问题</name>
<files>Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</files>
<read_first>Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift, ReadViewDemo/ReadViewDemo/ViewController.swift</read_first>
<action>对 fixed / interactive EPUB 路径做回归验证,利用现有 `RDEPUBWebViewDebug` 与 demo/runtime evidence 检查资源加载、页面渲染、外链/内部链接与 search decoration 相关行为。若发现明显回归(白屏、加载失败、关键资源缺失、渲染未完成),做最小修复并把结果写入 verification artifact。</action>
<acceptance_criteria>
- fixed or interactive sample path has explicit runtime evidence
- resource loading and rendering regressions are either fixed or clearly surfaced in verification
- demo/runtime logs remain sufficient to inspect failures without adding a second validation shell
</acceptance_criteria>
<verify>rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S</verify>
<done>WebKit-backed reader paths are included in the final regression closure, not treated as out-of-band.</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
- [ ] `rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift`
- [ ] `rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S`
</verification>
<output>
After completion, create `.planning/phases/05-regression-stability/05-02-SUMMARY.md` and `.planning/phases/05-regression-stability/05-VERIFICATION.md`
</output>

View File

@ -1,16 +0,0 @@
# 05-02 Summary
## Outcome
Wave 2 closed the remaining regression gap by exercising the new matrix and tightening the demo validator where it initially drifted from public APIs.
- fixed/interactive validation now resolves spine resources through public `RDEPUBResourceResolver` APIs and verifies fixed spreads with explicit `RDEPUBPreferences`
- TXT validation now builds a real `RDPlainTextBook` and records chapter/page evidence
- the existing native pagination and restore diagnostics remain active for reflowable books, so Phase 3-4 guarantees stay visible during Phase 5 runs
## Fixes
- removed demo-only calls to internal reader helpers (`makePreferences`, theme CSS accessors)
- removed the nonisolated `UIScreen.main` warning by passing the existing viewport size into fixed-layout validation
- kept the changes local to `ReadViewDemo/ReadViewDemo/ViewController.swift`; no new runtime architecture was introduced

View File

@ -1,27 +0,0 @@
# Phase 5: 回归验证与稳定性收敛 - Pattern Map
## Goal
把 Phase 5 约束成“样本矩阵 + reader 流程 + 收敛修复”的闭环,而不是泛泛地补测试。
## Planned Outputs
| Planned file | Role | Primary evidence | Why this is the right analog |
|--------------|------|------------------|------------------------------|
| `05-01-PLAN.md` | 构建样本矩阵、诊断手段和复现清单 | `ReadViewDemo/ViewController.swift`, `ReadViewDemo/book/`, `RDEPUBWebViewDebug`, prior verification artifacts | 这些是现有验证入口和日志基础。 |
| `05-02-PLAN.md` | 依据矩阵执行回归并收敛稳定性问题 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBWebView*`, `RDURLReaderController` | 这里汇聚了 Phase 1-4 改动后的主要风险表面。 |
## Code Evidence Map
| Concern | Closest source of truth | Evidence to extract |
|---------|-------------------------|---------------------|
| 样本入口与启动验证 | `ReadViewDemo/ReadViewDemo/ViewController.swift` | 已有 startup validation可扩展成矩阵摘要。 |
| native reflowable 风险 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBTextContentView` | 搜索、高亮、repagination、位置恢复都集中在这里。 |
| fixed / interactive 风险 | `RDEPUBWebView`, `RDEPUBWebView+FixedLayout`, `RDEPUBResourceURLSchemeHandler` | 这些路径依赖 WebKit/resource loading不同于 native text。 |
| TXT 支持风险 | `RDURLReaderController`, `RDPlainTextBookBuilder` | roadmap 要求 `.txt` 主流程不回归,但当前样本覆盖不足。 |
## Writing Guidance
- Phase 5 的计划必须明确区分“验证基础设施/矩阵”与“发现问题后的收敛修复”。
- 任何修复都应以回归证据驱动,不应重新打开更大范围的架构重构。
- 计划里要写清楚哪些样本书承担哪些验证职责。

View File

@ -1,122 +0,0 @@
# Phase 5: 回归验证与稳定性收敛 - Research
**Researched:** 2026-05-22
**Domain:** iOS EPUB/TXT regression validation / sample-corpus coverage / stability convergence
**Confidence:** HIGH
<user_constraints>
## User Constraints
No `CONTEXT.md` exists for this phase. Planning is based on roadmap requirements, the current codebase, and completed artifacts from Phases 1-4 only.
</user_constraints>
<research_summary>
## Summary
By the end of Phase 4, the codebase has meaningful diagnostics for both native text pagination and reader-state restore, but the validation surface is still uneven. Most current proof is either:
- source inspection (`rg`, architecture invariants)
- simulator build/run success
- demo startup logs from a small subset of sample books
That was enough to unblock development, but not enough to claim stability closure. Phase 5 therefore shifted from “implement capability” to “prove breadth and harden failures.”
The sample corpus used for execution now covers five local books:
- `《凡人修仙传》精校版全本.epub`
- `回归验证样本.txt`
- `宝山辽墓材料与释读.epub`
- `张学良传.epub`
- `爱忘事的熊爷爷.epub`
Observed behavior from Phases 3-4 shows these books are not equivalent:
- `宝山辽墓材料与释读.epub` exercises native reflowable pagination semantics and repagination restore checks
- `爱忘事的熊爷爷.epub` clearly drives fixed-layout / interactive-style WebKit resource flows in runtime logs
- `回归验证样本.txt` was added during execution so the TXT path is now part of the built-in demo corpus
The main Phase 5 planning implication is that validation has to be organized as a matrix, not a single “open the app and see it works” loop. At minimum the matrix must cover:
1. Native reflowable text with richer page metadata and repagination
2. Fixed layout / WebKit-based rendering and resource loading
3. TXT/native text book path through `RDURLReaderController`
4. Reader interaction flows: open, TOC jump, search navigation, highlight persistence, theme/font changes, and repeated repagination
The repo already has enough hooks to support this:
- `ReadViewDemo/ViewController.swift` reports sample validation summaries at startup
- `RDEPUBTextBookBuilder` records pagination/resource diagnostics
- `RDEPUBWebViewDebug` and resource-scheme logs expose WebKit/fixed-layout loading behavior
- `RDEPUBReaderController` centralizes repagination, restore, search, highlight, and viewport handling
What is missing is orchestration and closure:
- explicit mapping from sample book → risk category
- runtime assertions or summary output for reader flows beyond startup validation
- a disciplined path to take discovered regressions and either fix them or record them as resolved in the same phase
**Primary recommendation:** Plan Phase 5 as a validation-and-fix loop centered on a documented sample matrix and runtime evidence. The goal is not new architecture; it is repeatable proof that the main supported book categories and reader flows stay intact after the Phase 1-4 refactor.
Execution outcome:
- `ReadViewDemo` startup validation now emits matrix lines for complex reflowable, fixed/interactive WebKit, and TXT samples
- runtime evidence showed `样本验证5/5 通过`
- the demo remains the single validation entry point; no secondary harness was introduced
</research_summary>
<code_evidence>
## Code Evidence
| Concern | Source | Why it matters |
|--------|--------|----------------|
| Demo validation entry point | `ReadViewDemo/ReadViewDemo/ViewController.swift` | Already emits startup resource/pagination/restore summaries; best place to extend verification. |
| Native text diagnostics | `RDEPUBTextBookBuilder`, `RDEPUBTextLayouter`, `RDEPUBTextRendererSupport` | Existing metadata can drive stronger regression assertions. |
| Reader flow behavior | `RDEPUBReaderController` | Open, search, selection, highlight, TOC, repagination, and state restore all converge here. |
| Web/fixed runtime logs | `RDEPUBWebViewDebug`, `RDEPUBWebView`, `RDEPUBWebView+FixedLayout`, `RDEPUBResourceURLSchemeHandler` | Needed to prove non-native paths did not regress. |
| TXT path | `RDURLReaderController`, `RDPlainTextBookBuilder` | Supported but currently underrepresented in the sample corpus. |
</code_evidence>
<sample_corpus>
## Current Sample Corpus
| Sample | Likely category | Current evidence |
|--------|------------------|------------------|
| `宝山辽墓材料与释读.epub` | Native reflowable, complex blocks/images | Used in Phase 3/4 pagination and restore diagnostics. |
| `《凡人修仙传》精校版全本.epub` | Interactive/WebKit path | Runtime summary now classifies it as `webInteractive` and verifies `spine 2470 · missing 0`. |
| `回归验证样本.txt` | TXT/native text path | Added in Phase 5 and validated through `RDPlainTextBookBuilder` with startup summary output. |
| `张学良传.epub` | Reflowable nonfiction / alternate structure | Runtime summary classifies it under complex reflowable. |
| `爱忘事的熊爷爷.epub` | Fixed-layout / interactive-style WebKit path | Runtime log shows extensive fixed/resource loading. |
</sample_corpus>
<architecture_patterns>
## Architecture Patterns
### Pattern 1: Validate by book category, not by implementation layer
**What:** Organize checks around sample types and user flows.
**Why:** Stability issues are user-visible regressions, not just module-level failures.
### Pattern 2: Prefer evidence-producing diagnostics over ad hoc manual notes
**What:** Runtime summaries, assertions, and structured logs should back regression claims.
**Why:** Repeatability matters more than one-off successful runs.
### Pattern 3: Couple regression detection with fix closure
**What:** The phase should allow targeted source fixes where diagnostics surface concrete failures.
**Why:** A pure audit phase without remediation would leave the roadmap unfinished.
### Anti-Patterns to Avoid
- Treating build success as sufficient evidence of reader stability.
- Adding broad new architecture during a convergence phase.
- Expanding validation without documenting which sample proves which risk category.
</architecture_patterns>
<open_questions>
## Open Questions
- Should Phase 5 add a dedicated TXT sample into the demo corpus, or rely on an external local text file during execution?
- How much of the reader interaction validation should be automated in code/logs versus performed manually on simulator?
- If a regression is found in fixed/interactive WebKit flows, should it be fixed within Phase 5 or only documented if the root cause is outside the current scope?
</open_questions>

View File

@ -1,46 +0,0 @@
# Phase 5: 回归验证与稳定性收敛 - Validation
## Required Checks
- 至少覆盖三类样本:纯文本/TXT 或小说型 reflowable、复杂块元素/图片 reflowable、fixed/interactive WebKit 路径
- `.epub``.txt` 的 reader 主流程都必须有明确验证路径
- 每个重要 reader 流程至少有一种可重复证据:日志、断言、摘要、或稳定的手动检查清单
## Executed Matrix
| Sample | Category | Evidence |
|--------|----------|----------|
| `宝山辽墓材料与释读.epub` | 复杂图文 / native reflowable | 启动摘要 + pagination/restore diagnostics |
| `张学良传.epub` | 复杂图文 / native reflowable | 启动摘要 + active native reader UI evidence |
| `《凡人修仙传》精校版全本.epub` | interactive / WebKit | 启动摘要 `webInteractive` + `missing 0` |
| `爱忘事的熊爷爷.epub` | fixed layout / WebKit | 启动摘要 `webFixedLayout` + `spread` validation |
| `回归验证样本.txt` | TXT | 启动摘要 `TXT 验证` + page/chapter counts |
## Verification Anchors
### Source-level
- `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
- `rg -n "validation|diagnostic|restore|search|highlight|location|theme|font|fixed|resource" ReadViewDemo/ReadViewDemo Sources/RDReaderView -S`
- `rg -n "RDEPUBWebViewDebug|logSchemeTask|rendered|page break|restore" Sources/RDReaderView -S`
### Runtime / demo
- run `ReadViewDemo` on simulator
- capture startup validation summaries for every relevant sample category
- exercise at least one native reflowable flow with search/highlight/repagination
- exercise at least one fixed/interactive EPUB flow and confirm no loading/resource regression
- exercise a TXT reading flow if a sample exists or is added during execution
## Current Rerun Path
1. Launch `ReadViewDemo` on the configured simulator.
2. Read the startup summary from the on-screen status area or runtime log.
3. Confirm the matrix lines for `复杂图文`、`Fixed/互动`、`TXT`.
4. Open any listed sample and use the existing reader controls to recheck TOC, bookmark/highlight, and theme/font adjustments as needed.
## Completion Standard
Phase 5 planning is only good enough if each plan names:
1. which sample categories it covers
2. what evidence proves success
3. how discovered regressions are routed into concrete fixes

View File

@ -1,47 +0,0 @@
# Phase 5 Verification
## Commands
- `build_sim` for scheme `ReadViewDemo`
- `build_run_sim` for scheme `ReadViewDemo`
- `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
- `rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S`
- `rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift`
- `rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S`
- `snapshot_ui` on the running simulator
## Results
- Simulator build succeeded with no warnings
- Simulator launch succeeded
- Demo corpus now includes 5 verifiable books:
- `《凡人修仙传》精校版全本.epub`
- `回归验证样本.txt`
- `宝山辽墓材料与释读.epub`
- `张学良传.epub`
- `爱忘事的熊爷爷.epub`
- Source verification confirms:
- `ViewController` now validates `.txt`, native reflowable EPUB, and fixed/interactive EPUB in one pass
- native reader flows for search, highlight, location restore, bookmarks, TOC, and theme/font repagination remain centered in `RDEPUBReaderController`
- WebKit-backed flows still expose resource/render lifecycle evidence through `RDEPUBWebViewDebug` and `RDEPUBResourceURLSchemeHandler`
## Runtime Evidence
From the app runtime log:
- `样本验证5/5 通过`
- `矩阵[复杂图文] 2/2 · 宝山辽墓材料与释读(textReflowable)、张学良传(textReflowable)`
- `矩阵[Fixed/互动] 2/2 · 《凡人修仙传》精校版全本(webInteractive)、爱忘事的熊爷爷(webFixedLayout)`
- `矩阵[TXT] 1/1 · 回归验证样本(txt)`
- `Web 路径验证:《凡人修仙传》精校版全本 · profile webInteractive · spine 2470 · missing 0`
- `Web 路径验证:爱忘事的熊爷爷 · profile webFixedLayout · spine 22 · missing 0`
From simulator UI inspection:
- the app was running inside `RDEPUBReaderController` with visible reader controls (`epub.reader.back`, `epub.reader.toc`, `epub.reader.bookmarks`, `epub.reader.highlights`, `epub.reader.settings`)
- the current native reflowable sample exposed page state (`2 / 691`) and standard reader chrome without a blank page or stalled render
## Residual Risk
- The phase now has corpus-level runtime evidence for TXT and WebKit paths, but not a fully automated UI walkthrough for opening every sample and driving every interaction on simulator
- Simulator runtime still emits pre-existing CoreText font substitution notes for `.SFUI-Semibold`; Phase 5 did not change font selection code and the app remained functional despite the warning