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,56 +1,47 @@
import Foundation
///
///
///
/// -
/// -
struct RDEPUBJumpSession {
/// spineIndex
let anchorSpineIndex: Int
///
let createdAt: CFAbsoluteTime
/// spineIndex
let protectedSpineIndices: Set<Int>
///
let sequenceNumber: Int
///
let expiresAt: CFAbsoluteTime
///
let reason: Reason
///
enum Reason {
case tableOfContentsJump
case bookmarkJump
case searchJump
}
///
enum EndReason {
///
case coverageComplete
///
case navigatedAway
///
case timeout
///
case superseded
}
}
/// JumpSession
public struct RDEPUBJumpSessionPolicy: Equatable {
///
public let exitPageThreshold: Int
///
public let timeout: TimeInterval
///
public let idleGracePeriod: TimeInterval
///
public let protectedNeighborRadius: Int
///
public static let `default` = RDEPUBJumpSessionPolicy(
exitPageThreshold: 6,
timeout: 20,
@@ -71,26 +62,20 @@ public struct RDEPUBJumpSessionPolicy: Equatable {
}
}
/// JumpSession
final class RDEPUBJumpSessionManager {
private unowned let context: RDEPUBReaderContext
/// JumpSession
private(set) var activeSession: RDEPUBJumpSession?
///
private var nextSequenceNumber: Int = 0
///
private var consecutivePageCount: Int = 0
///
private var lastPageDirection: PageDirection?
///
private var lastActivityTime: CFAbsoluteTime = 0
///
enum PageDirection {
case forward
case backward
@@ -100,7 +85,6 @@ final class RDEPUBJumpSessionManager {
self.context = context
}
/// JumpSession
@discardableResult
func createSession(
anchorSpineIndex: Int,
@@ -110,7 +94,6 @@ final class RDEPUBJumpSessionManager {
let policy = context.configuration.jumpSessionPolicy
let now = CFAbsoluteTimeGetCurrent()
//
var protectedIndices: Set<Int> = [anchorSpineIndex]
for offset in 1...policy.protectedNeighborRadius {
let lower = anchorSpineIndex - offset
@@ -146,7 +129,6 @@ final class RDEPUBJumpSessionManager {
return session
}
///
func recordPageChange(fromSpineIndex: Int, toSpineIndex: Int) {
guard activeSession != nil else { return }
@@ -161,29 +143,24 @@ final class RDEPUBJumpSessionManager {
}
}
///
func shouldAllowPageMapTakeover(candidateSpineIndices: Set<Int>) -> Bool {
guard let session = activeSession else {
return true // Session
return true
}
//
let protectedIndices = session.protectedSpineIndices
let coverageRatio = Double(protectedIndices.intersection(candidateSpineIndices).count) /
Double(protectedIndices.count)
// 80%
return coverageRatio >= 0.8
}
/// Session
func checkSessionEnd(currentSpineIndex: Int, isIdle: Bool) -> RDEPUBJumpSession.EndReason? {
guard let session = activeSession else { return nil }
let now = CFAbsoluteTimeGetCurrent()
let policy = context.configuration.jumpSessionPolicy
// 1.
if now >= session.expiresAt {
if isIdle || (now - lastActivityTime) >= policy.idleGracePeriod {
RDEPUBBackgroundTrace.log(
@@ -194,7 +171,6 @@ final class RDEPUBJumpSessionManager {
}
}
// 2.
if !session.protectedSpineIndices.contains(currentSpineIndex) {
if consecutivePageCount >= policy.exitPageThreshold {
RDEPUBBackgroundTrace.log(
@@ -204,14 +180,13 @@ final class RDEPUBJumpSessionManager {
return .navigatedAway
}
} else {
//
consecutivePageCount = 0
}
return nil
}
/// Session
func endSession(_ reason: RDEPUBJumpSession.EndReason) {
guard let session = activeSession else { return }
RDEPUBBackgroundTrace.log(
@@ -223,7 +198,6 @@ final class RDEPUBJumpSessionManager {
lastPageDirection = nil
}
/// Session
func clearSession() {
activeSession = nil
consecutivePageCount = 0