8.3 KiB
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.paginatePublicationbranches between nativeRDEPUBTextBookBuilder, fixed-layout snapshots, andRDEPUBPaginatorWebKit measurement.applyTextBook,finishPagination,restoreReadingLocation, andcurrentVisibleLocationalready contain a parallel text-book path that bypassesRDEPUBReadingSession.pageIndex(for:).pageContentView(readerView:pageNum:containerView:)already swaps betweenRDEPUBTextContentViewandRDEPUBWebContentView.- Search uses
RDEPUBTextSearchEnginewhentextBookexists andRDEPUBHTMLSearchEngineotherwise. - Selection/highlight persistence for native text still depends on
RDEPUBTextOffsetRangeInfoabsolute 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:
- Reflowable native pages remain rendered through
RDEPUBTextBook. RDEPUBReaderControllerkeeps one coherent restore/navigation/search/highlight API no matter whether the source page is native text or Web content.- 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.restoreReadingLocationandcurrentVisibleLocationuse separate logic for text-book vs snapshot pages.normalizedTextSelectionrecomputes progression from absolute offset payloads; regressions here would silently break persisted highlights.refreshVisibleContentPreservingLocationandrebuildExternalTextBookcan preserve location only if the text-book lookup contract remains stable after repagination.flattenedTableOfContentsItemsandpageNumber(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
RDReaderViewto 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
RDEPUBReadingSessionworth extending to cover nativeRDEPUBTextBookpage 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>