chore: checkpoint current milestone work
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
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*
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
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*
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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*
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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,只能作为“不要复用”的反例。
|
||||
@@ -0,0 +1,177 @@
|
||||
# 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>
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
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*
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,21 @@
|
||||
# 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
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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 真正落盘到模型里。
|
||||
@@ -0,0 +1,144 @@
|
||||
# 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>
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,15 @@
|
||||
# 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
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,29 @@
|
||||
# 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。
|
||||
@@ -0,0 +1,123 @@
|
||||
# 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>
|
||||
@@ -0,0 +1,29 @@
|
||||
# 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
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,21 @@
|
||||
# 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.
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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 的计划必须明确区分“验证基础设施/矩阵”与“发现问题后的收敛修复”。
|
||||
- 任何修复都应以回归证据驱动,不应重新打开更大范围的架构重构。
|
||||
- 计划里要写清楚哪些样本书承担哪些验证职责。
|
||||
@@ -0,0 +1,122 @@
|
||||
# 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>
|
||||
@@ -0,0 +1,46 @@
|
||||
# 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
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user