feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化

- 实现EPUB阅读器搜索功能及选中注释功能
- 优化CFI模块,修复代码审查发现的11个问题
- 实现大书远距目录跳转与后台补全优化方案
- 优化设置面板与章节运行时联动
- 重构及大量改进优化
This commit is contained in:
shenlei
2026-06-22 20:26:34 +08:00
parent f50495ad91
commit c65c190b71
178 changed files with 11380 additions and 6728 deletions
@@ -1,7 +1,7 @@
import Foundation
///
final class RDEPUBReaderSearchCoordinator {
private unowned let context: RDEPUBReaderContext
init(context: RDEPUBReaderContext) {
@@ -12,8 +12,6 @@ final class RDEPUBReaderSearchCoordinator {
context.controller
}
///
/// - Parameter keyword:
func search(keyword: String) {
guard let controller else { return }
let normalizedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -37,21 +35,16 @@ final class RDEPUBReaderSearchCoordinator {
}
}
///
/// - Returns:
@discardableResult
func searchNext() -> Bool {
advanceSearch(by: 1)
}
///
/// - Returns:
@discardableResult
func searchPrevious() -> Bool {
advanceSearch(by: -1)
}
///
@discardableResult
func selectSearchMatch(at index: Int) -> Bool {
guard let controller else { return false }
@@ -66,7 +59,6 @@ final class RDEPUBReaderSearchCoordinator {
return navigateToCurrentSearchMatch(animated: true)
}
///
func clearSearch() {
guard let controller else { return }
controller.searchState = nil
@@ -74,9 +66,6 @@ final class RDEPUBReaderSearchCoordinator {
controller.refreshVisibleContentPreservingLocation()
}
///
/// - Parameter page:
/// - Returns: nil
func searchPresentation(for page: EPUBPage) -> RDEPUBSearchPresentation? {
guard let controller else { return nil }
guard let searchState = controller.searchState,
@@ -175,6 +164,7 @@ final class RDEPUBReaderSearchCoordinator {
let progressionDenominator = max(fullLength - 1, 1)
let progression = Double(foundRange.location) / Double(progressionDenominator)
let rangeAnchor = chapterData.rangeAnchor(for: foundRange)
matches.append(
RDEPUBSearchMatch(
href: normalizedHref,
@@ -183,7 +173,9 @@ final class RDEPUBReaderSearchCoordinator {
localMatchIndex: localMatchIndex,
rangeLocation: foundRange.location,
rangeLength: foundRange.length,
rangeAnchor: chapterData.rangeAnchor(for: foundRange)
rangeAnchor: rangeAnchor,
cfi: chapterData.indexTable.cfi(for: rangeAnchor.start)?.rawValue,
rangeCFI: chapterData.indexTable.cfiRange(for: rangeAnchor)?.rawValue
)
)
@@ -210,6 +202,7 @@ final class RDEPUBReaderSearchCoordinator {
title: runtimeChapter.title,
attributedContent: runtimeChapter.typesetAttributedString,
fragmentOffsets: runtimeChapter.chapterOffsetMap.fragmentOffsets,
cfiMap: runtimeChapter.chapterOffsetMap.cfiMap,
pageBreakReasons: runtimeChapter.pages.map(\.metadata.breakReason),
pages: runtimeChapter.pages
)
@@ -266,7 +259,9 @@ final class RDEPUBReaderSearchCoordinator {
progression: searchMatch.progression,
lastProgression: searchMatch.progression,
fragment: nil,
rangeAnchor: searchMatch.rangeAnchor
rangeAnchor: searchMatch.rangeAnchor,
cfi: searchMatch.cfi,
rangeCFI: searchMatch.rangeCFI
)
return controller.restoreReadingLocation(location, animated: animated)
}
@@ -298,7 +293,9 @@ final class RDEPUBReaderSearchCoordinator {
progression: searchMatch.progression,
lastProgression: searchMatch.progression,
fragment: nil,
rangeAnchor: searchMatch.rangeAnchor
rangeAnchor: searchMatch.rangeAnchor,
cfi: searchMatch.cfi,
rangeCFI: searchMatch.rangeCFI
)
if let textBook = controller.textBook, let publication = controller.publication {