docs: start milestone v1.1 WXRead 深化对齐
This commit is contained in:
parent
1498e83ace
commit
bd4978266a
@ -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
76
.planning/REQUIREMENTS.md
Normal 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*
|
||||
@ -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 | - |
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
@ -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*
|
||||
@ -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>
|
||||
@ -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 semantics,Phase 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*
|
||||
@ -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 现状对齐。
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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*
|
||||
@ -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` 容器契约都保持不变。
|
||||
@ -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 semantics;Fixed Layout、交互式 EPUB 以及 `RDReaderView` 容器都应保持边界不变。
|
||||
@ -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>
|
||||
@ -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 output,Plan 02 可以直接把 `RDEPUBDTCoreTextRenderer` 接到这个新契约上。
|
||||
|
||||
---
|
||||
*Phase: 02-typesetter-css*
|
||||
*Completed: 2026-05-22*
|
||||
@ -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>
|
||||
@ -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*
|
||||
@ -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>
|
||||
@ -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*
|
||||
@ -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,只能作为“不要复用”的反例。
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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*
|
||||
@ -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>
|
||||
@ -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.
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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.
|
||||
@ -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 真正落盘到模型里。
|
||||
@ -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>
|
||||
@ -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 task’s `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
|
||||
@ -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.
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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.
|
||||
@ -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。
|
||||
@ -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 4’s 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 user’s 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>
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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.
|
||||
|
||||
@ -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>
|
||||
@ -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
|
||||
|
||||
@ -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 的计划必须明确区分“验证基础设施/矩阵”与“发现问题后的收敛修复”。
|
||||
- 任何修复都应以回归证据驱动,不应重新打开更大范围的架构重构。
|
||||
- 计划里要写清楚哪些样本书承担哪些验证职责。
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
Reference in New Issue
Block a user