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,58 +1,46 @@
// RDEPUBReadingSession.swift
// EPUB
// initializing/loading/idle/jumping/moving/repaginating
//
//
import Foundation
/// EPUB
/// initializing loading idle jumping/moving/repaginating
public final class RDEPUBReadingSession {
/// +
public typealias PaginationSnapshot = (pages: [EPUBPage], chapters: [EPUBChapterInfo])
///
public let publication: RDEPUBPublication
///
public private(set) var navigatorState: RDEPUBNavigatorState = .initializing
///
public private(set) var activePages: [EPUBPage] = []
///
public private(set) var activeChapters: [EPUBChapterInfo] = []
///
public private(set) var stagedPages: [EPUBPage]?
///
public private(set) var stagedChapters: [EPUBChapterInfo]?
///
public private(set) var stagedRestoreLocation: RDEPUBLocation?
/// fragment
public private(set) var pendingNavigationLocation: RDEPUBLocation?
///
public private(set) var pendingNavigationPageNum: Int?
/// Range Web 使
public private(set) var pendingNavigationHighlightRangeInfo: String?
///
public private(set) var currentViewport: RDEPUBViewport?
/// + + +
public private(set) var currentReadingContext: RDEPUBReadingContext?
public init(publication: RDEPUBPublication) {
self.publication = publication
}
/// 访
public var resourceResolver: RDEPUBResourceResolver {
publication.resourceResolver
}
///
public func transition(to state: RDEPUBNavigatorState) {
navigatorState = state
}
/// initializing
public func resetRuntimeState() {
navigatorState = .initializing
activePages = []
@@ -63,20 +51,17 @@ public final class RDEPUBReadingSession {
currentReadingContext = nil
}
///
public func setActiveSnapshot(_ snapshot: PaginationSnapshot) {
activePages = snapshot.pages
activeChapters = snapshot.chapters
}
///
public func stageSnapshot(_ snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?) {
stagedPages = snapshot.pages
stagedChapters = snapshot.chapters
stagedRestoreLocation = restoreLocation
}
///
public func stagedSnapshot() -> PaginationSnapshot? {
guard let stagedPages, let stagedChapters else {
return nil
@@ -84,7 +69,6 @@ public final class RDEPUBReadingSession {
return (stagedPages, stagedChapters)
}
///
public func consumeStagedSnapshotIfAllowed() -> (snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?)? {
guard navigatorState.isStableForSnapshotApplication,
let stagedPages,
@@ -96,21 +80,18 @@ public final class RDEPUBReadingSession {
return ((stagedPages, stagedChapters), restoreLocation)
}
///
public func clearStagedSnapshot() {
stagedPages = nil
stagedChapters = nil
stagedRestoreLocation = nil
}
///
public func clearPendingNavigation() {
pendingNavigationLocation = nil
pendingNavigationPageNum = nil
pendingNavigationHighlightRangeInfo = nil
}
/// spine
public func pendingLocation(forPageNumber pageNumber: Int, spineIndex: Int?) -> RDEPUBLocation? {
guard pendingNavigationPageNum == pageNumber,
let pendingNavigationLocation else {
@@ -126,7 +107,6 @@ public final class RDEPUBReadingSession {
return pendingNavigationLocation
}
/// Range
public func pendingHighlightRangeInfo(forPageNumber pageNumber: Int, spineIndex: Int?) -> String? {
guard pendingNavigationPageNum == pageNumber,
let pendingNavigationHighlightRangeInfo,
@@ -146,7 +126,6 @@ public final class RDEPUBReadingSession {
return pendingNavigationHighlightRangeInfo
}
/// spine spread
public func pageContains(spineIndex: Int, in page: EPUBPage) -> Bool {
if let fixedSpread = page.fixedSpread {
return fixedSpread.resources.contains(where: { $0.spineIndex == spineIndex })
@@ -154,7 +133,6 @@ public final class RDEPUBReadingSession {
return page.spineIndex == spineIndex
}
/// 退
public func fallbackLocation(for page: EPUBPage, bookIdentifier: String?) -> RDEPUBLocation? {
if let fixedSpread = page.fixedSpread {
return RDEPUBLocation(
@@ -184,7 +162,6 @@ public final class RDEPUBReadingSession {
)
}
///
public func currentVisibleLocation(currentPageNumber: Int, bookIdentifier: String?) -> RDEPUBLocation? {
guard currentPageNumber > 0,
activePages.indices.contains(currentPageNumber - 1) else {
@@ -193,7 +170,6 @@ public final class RDEPUBReadingSession {
return fallbackLocation(for: activePages[currentPageNumber - 1], bookIdentifier: bookIdentifier)
}
/// spine
public func initialSpineIndex(for location: RDEPUBLocation?) -> Int {
guard let location,
let normalizedHref = resourceResolver.normalizedHref(location.href),
@@ -203,7 +179,6 @@ public final class RDEPUBReadingSession {
return spineIndex
}
///
public func pageIndex(for location: RDEPUBLocation, bookIdentifier: String?) -> Int? {
guard location.bookIdentifier == nil || location.bookIdentifier == bookIdentifier else {
return nil
@@ -244,7 +219,6 @@ public final class RDEPUBReadingSession {
return candidates[localIndex].offset
}
/// jumping
public func queueNavigation(
to location: RDEPUBLocation,
relativeToSpineIndex spineIndex: Int? = nil,
@@ -271,7 +245,6 @@ public final class RDEPUBReadingSession {
return pageIndex + 1
}
/// idle
public func updateReadingContext(
pageNumber: Int,
location: RDEPUBLocation,
@@ -320,13 +293,10 @@ public final class RDEPUBReadingSession {
}
}
/// 退
public func currentReadingLocation(bookIdentifier: String?) -> RDEPUBLocation? {
currentReadingContext?.location ?? currentVisibleLocation(currentPageNumber: currentViewport?.visiblePageNumber ?? 0, bookIdentifier: bookIdentifier)
}
///
/// spread spine
public func makePaginationSnapshot(
pageCounts: [Int],
preferences: RDEPUBPreferences,