ReadViewSDK/.planning/phases/08-pagination-quality-cache-performance/08-01-PLAN.md
shen 86e6922f7b docs(08): create phase plan — image display fix, cache stub, perf sampling
Three plans for Phase 8 (分页质量、缓存与性能采样):
- 08-01: Pagination cache key struct and manager stub (QUAL-01)
- 08-02: Unified 1080x1920 max-size constraint for all images, footnote width-only sizing (QUAL-02, QUAL-03, QUAL-04)
- 08-03: Pagination timing instrumentation with [EPUB][Perf] diagnostic output (QUAL-04)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 18:30:56 +08:00

123 lines
5.6 KiB
Markdown

---
phase: 08-pagination-quality-cache-performance
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift
autonomous: true
requirements:
- QUAL-01
must_haves:
truths:
- "Cache key struct exists that captures viewport + typography configuration"
- "Cache lookup and store methods exist on the cache type"
- "Build succeeds with new cache file in project"
artifacts:
- path: "Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift"
provides: "Cache key struct and cache manager stub"
exports: ["RDEPUBTextRendererCache", "RDEPUBPaginationCacheKey"]
key_links:
- from: "Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift"
to: "RDEPUBDTCoreTextRenderer"
via: "import (future wiring)"
pattern: "RDEPUBTextRendererCache"
---
<objective>
Create the pagination cache infrastructure: a cache key struct that captures viewport dimensions and typography configuration, plus a cache manager stub with lookup/store/invalidate methods. This is the structural foundation that plan 08-02 and future work will wire into the actual rendering pipeline.
Purpose: Prevents the same chapter from being fully re-typeset when viewport and typography config haven't changed (QUAL-01).
Output: `RDEPUBTextRendererCache.swift` with cache key struct and manager stub.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08-pagination-quality-cache-performance/08-CONTEXT.md
@.planning/phases/08-pagination-quality-cache-performance/08-RESEARCH.md
<interfaces>
<!-- Existing types the executor needs to know about -->
From Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift:
- `RDEPUBTextChapterRenderRequest` — contains `style: RDEPUBTextRenderStyle`, `context` with baseURL
- `RDEPUBTextRenderStyle` — has `font: UIFont`, `lineSpacing: CGFloat`, `textColor: UIColor?`, `backgroundColor: UIColor?`
From Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift:
- `RDEPUBTextRenderStyle` properties used in rendering: `font.pointSize`, `font.familyName`, `font.fontName`, `lineSpacing`
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create cache key struct and cache manager stub</name>
<files>Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift</files>
<read_first>
- Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift (understand render request and style types)
- Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift (understand what configuration affects pagination output)
</read_first>
<action>
Create `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift` with:
1. `RDEPUBPaginationCacheKey` struct — captures the inputs that determine pagination output:
- `chapterID: String` (unique chapter identifier, e.g. href or file path)
- `viewportSize: CGSize` (the available page dimensions)
- `fontDescriptor: String` (font family + name + pointSize, concatenated)
- `lineSpacing: CGFloat`
- `textColorHash: Int?` (hash of textColor, if present)
- Implement `Equatable` conformance (auto-synthesize is fine)
2. `RDEPUBTextRendererCache` class:
- `static let shared = RDEPUBTextRendererCache()`
- `private var cache: [RDEPUBPaginationCacheKey: [NSAttributedString]]` — maps key to array of page attributed strings
- `func cachedPages(for key: RDEPUBPaginationCacheKey) -> [NSAttributedString]?` — returns cached pages or nil
- `func store(pages: [NSAttributedString], for key: RDEPUBPaginationCacheKey)` — stores pages
- `func invalidate(for key: RDEPUBPaginationCacheKey)` — removes a specific entry
- `func invalidateAll()` — clears entire cache
- `var entryCount: Int` — read-only count of cached entries (for diagnostics)
Make the class `final` and `Sendable`-safe (use a lock or `@MainActor` annotation since rendering is main-thread). Use `NSLock` for thread safety.
Do NOT wire this cache into the rendering pipeline yet — that is future work. This plan only creates the infrastructure.
</action>
<verify>
<automated>xcodebuild build -project ReadViewDemo/ReadViewDemo.xcodeproj -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 16' 2>&1 | tail -5</automated>
</verify>
<acceptance_criteria>
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererCache.swift` exists
- File contains `struct RDEPUBPaginationCacheKey: Equatable` with properties: chapterID, viewportSize, fontDescriptor, lineSpacing, textColorHash
- File contains `final class RDEPUBTextRendererCache` with static `shared` instance
- Class has methods: `cachedPages(for:)`, `store(pages:for:)`, `invalidate(for:)`, `invalidateAll()`
- Class has computed property `entryCount: Int`
- Build succeeds (xcodebuild exits 0)
</acceptance_criteria>
<done>Cache key struct and cache manager stub exist, build compiles successfully</done>
</task>
</tasks>
<verification>
- Build succeeds with new file
- Cache types are properly defined and accessible from other modules in the same target
</verification>
<success_criteria>
- RDEPUBPaginationCacheKey captures all inputs that affect pagination output
- RDEPUBTextRendererCache provides a thread-safe, singleton cache surface ready for wiring
- No existing code is modified — this is purely additive
</success_criteria>
<output>
Create `.planning/phases/08-pagination-quality-cache-performance/08-01-SUMMARY.md` when done
</output>