feat: chapter runtime refactoring and related updates

- Refactor chapter runtime: replace window coordinator/snapshot with warmup orchestrator
- Update EPUB core: parser, reading session, JS bridge, navigator layout
- Update reader controller: data source, location resolution, persistence
- Update chapter runtime: data cache, loader, runtime store, disk cache, warmup orchestrator
- Remove deprecated navigation state machine and pagination state
- Update text rendering: book cache, HTML normalizer
- Update UI: text content view, dark image adjuster, text selection controller
- Update settings and reader configuration
- Add CODE_REVIEW.md and AUDIT_FINAL.md documentation
- Update pod dependencies (remove SSAlertSwift, SnapKit)
- Update podspec and pod configuration files

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-06-26 18:50:07 +09:00
co-authored by Claude
parent b8aa10c535
commit 22e7e44220
123 changed files with 3701 additions and 9118 deletions
@@ -1,5 +1,14 @@
import UIKit
/// Captures layout parameters on the main thread for safe use on background queues.
/// Create via `RDEPUBReaderContext.makeLayoutSnapshot()` before dispatching work off the main thread.
struct RDEPUBLayoutSnapshot {
let pageSize: CGSize
let style: RDEPUBTextRenderStyle
let layoutConfig: RDEPUBTextLayoutConfig
let renderSignature: String
}
final class RDEPUBReaderContext {
private let activityLock = NSLock()
@@ -73,11 +82,6 @@ final class RDEPUBReaderContext {
set { state.paginationToken = newValue }
}
var paginator: RDEPUBPaginator? {
get { state.paginator }
set { state.paginator = newValue }
}
var searchState: RDEPUBSearchState? {
get { state.searchState }
set { state.searchState = newValue }
@@ -159,43 +163,57 @@ final class RDEPUBReaderContext {
}
func currentPreferences() -> RDEPUBPreferences {
configuration.makePreferences()
let safeInsets = controller?.view.safeAreaInsets ?? .zero
return configuration.makePreferences(safeAreaInsets: safeInsets)
}
/// Captures all layout parameters needed for background chapter loading.
/// Must be called on the main thread. The returned snapshot is safe to use on any thread.
func makeLayoutSnapshot() -> RDEPUBLayoutSnapshot {
dispatchPrecondition(condition: .onQueue(.main))
let pageSize = currentTextPageSize()
let style = currentTextRenderStyle()
let layoutConfig = currentTextLayoutConfig(pageSize: pageSize)
let renderSignature = renderSignature(style: style, pageSize: pageSize, layoutConfig: layoutConfig)
return RDEPUBLayoutSnapshot(
pageSize: pageSize,
style: style,
layoutConfig: layoutConfig,
renderSignature: renderSignature
)
}
func currentTextPageSize() -> CGSize {
if Thread.isMainThread {
let pageNum = (readerView?.currentPage ?? -1) >= 0 ? readerView?.currentPage : nil
if let readerView, let pageNum {
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
if resolvedSize.width > 0, resolvedSize.height > 0 {
return resolvedSize
}
// M-04: Use dispatchPrecondition instead of assert so it's enforced in Release builds too.
dispatchPrecondition(condition: .onQueue(.main))
let pageNum = (readerView?.currentPage ?? -1) >= 0 ? readerView?.currentPage : nil
if let readerView, let pageNum {
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
if resolvedSize.width > 0, resolvedSize.height > 0 {
return resolvedSize
}
let viewportSize = currentLayoutContext().viewportSize
if viewportSize.width > 0, viewportSize.height > 0 {
return viewportSize
}
} else if let lastTextPaginationPageSize,
lastTextPaginationPageSize.width > 0,
lastTextPaginationPageSize.height > 0 {
}
let viewportSize = currentLayoutContext().viewportSize
if viewportSize.width > 0, viewportSize.height > 0 {
return viewportSize
}
if let lastTextPaginationPageSize,
lastTextPaginationPageSize.width > 0,
lastTextPaginationPageSize.height > 0 {
return lastTextPaginationPageSize
} else {
let mainThreadSize = DispatchQueue.main.sync { [weak self] in
self?.currentTextPageSize() ?? .zero
}
if mainThreadSize.width > 0, mainThreadSize.height > 0 {
return mainThreadSize
}
}
return environment.fallbackViewportSize
}
func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
environment.currentTextRenderStyle(configuration: configuration)
dispatchPrecondition(condition: .onQueue(.main))
return environment.currentTextRenderStyle(configuration: configuration)
}
func currentTextLayoutConfig(pageSize: CGSize) -> RDEPUBTextLayoutConfig {
environment.currentTextLayoutConfig(configuration: configuration, pageSize: pageSize)
dispatchPrecondition(condition: .onQueue(.main))
return environment.currentTextLayoutConfig(configuration: configuration, pageSize: pageSize)
}
func resolvedTextRenderer() -> RDEPUBTextRenderer {
@@ -282,10 +300,19 @@ final class RDEPUBReaderContext {
}
func currentRenderSignature() -> String {
dispatchPrecondition(condition: .onQueue(.main))
let style = currentTextRenderStyle()
let pageSize = currentTextPageSize()
let layoutConfig = currentTextLayoutConfig(pageSize: pageSize)
return [
return renderSignature(style: style, pageSize: pageSize, layoutConfig: layoutConfig)
}
private func renderSignature(
style: RDEPUBTextRenderStyle,
pageSize: CGSize,
layoutConfig: RDEPUBTextLayoutConfig
) -> String {
[
style.font.fontName,
"\(style.font.pointSize)",
"\(configuration.lineHeightMultiple)",