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,19 +1,15 @@
import Foundation
///
///
///
struct RDEPUBBackgroundPriorityPolicy {
///
let hotRadius: Int
///
let warmRadius: Int
///
let maxWarmJumpAnchors: Int
///
let coldLaneShare: Double
///
static let `default` = RDEPUBBackgroundPriorityPolicy(
hotRadius: 24,
warmRadius: 96,
@@ -21,7 +17,6 @@ struct RDEPUBBackgroundPriorityPolicy {
coldLaneShare: 0.15
)
///
static func adaptive(totalBuildableChapters: Int) -> RDEPUBBackgroundPriorityPolicy {
let hotRadius = min(max(12, Int(sqrt(Double(totalBuildableChapters)))), 48)
let warmRadius = min(max(hotRadius * 3, 32), 192)
@@ -34,7 +29,6 @@ struct RDEPUBBackgroundPriorityPolicy {
}
}
///
enum RDEPUBPriorityBand: Int, Comparable {
case hot = 0
case warmPrimary = 1
@@ -46,39 +40,32 @@ enum RDEPUBPriorityBand: Int, Comparable {
}
}
///
struct RDEPUBWarmJumpAnchor {
let spineIndex: Int
let timestamp: CFAbsoluteTime
let sequenceNumber: Int
}
///
struct RDEPUBMetadataParseWorkItem {
let spineIndex: Int
let generation: Int
let priorityBand: RDEPUBPriorityBand
///
var sortKey: (bandRank: Int, distanceToCurrent: Int, distanceToNewestJump: Int, spineIndex: Int) {
(priorityBand.rawValue, 0, 0, spineIndex)
}
}
///
final class RDEPUBBackgroundPriorityManager {
private unowned let context: RDEPUBReaderContext
///
private(set) var policy: RDEPUBBackgroundPriorityPolicy
///
private var warmAnchors: [RDEPUBWarmJumpAnchor] = []
/// generation
private(set) var currentGeneration: Int = 0
///
private var coldCursor: Int = 0
init(context: RDEPUBReaderContext) {
@@ -86,12 +73,10 @@ final class RDEPUBBackgroundPriorityManager {
self.policy = .default
}
///
func updatePolicy(_ newPolicy: RDEPUBBackgroundPriorityPolicy) {
policy = newPolicy
}
///
func addWarmAnchor(spineIndex: Int) {
let anchor = RDEPUBWarmJumpAnchor(
spineIndex: spineIndex,
@@ -101,13 +86,12 @@ final class RDEPUBBackgroundPriorityManager {
warmAnchors.insert(anchor, at: 0)
// N
if warmAnchors.count > policy.maxWarmJumpAnchors {
warmAnchors = Array(warmAnchors.prefix(policy.maxWarmJumpAnchors))
}
currentGeneration += 1
coldCursor = 0 //
coldCursor = 0
RDEPUBBackgroundTrace.log(
"PriorityManager",
@@ -115,7 +99,6 @@ final class RDEPUBBackgroundPriorityManager {
)
}
/// spineIndex
func makeMetadataPriorityOrder(
allBuildableIndices: [Int],
currentSpineIndex: Int?,
@@ -124,7 +107,6 @@ final class RDEPUBBackgroundPriorityManager {
let uncachedIndices = allBuildableIndices.filter { !cachedSpineIndices.contains($0) }
guard !uncachedIndices.isEmpty else { return [] }
// spineIndex
let items = uncachedIndices.map { spineIndex -> (spineIndex: Int, band: RDEPUBPriorityBand) in
let band = classifySpineIndex(
spineIndex: spineIndex,
@@ -133,33 +115,29 @@ final class RDEPUBBackgroundPriorityManager {
return (spineIndex, band)
}
//
let sorted = items.sorted { lhs, rhs in
//
if lhs.band != rhs.band {
return lhs.band < rhs.band
}
//
let lhsDistanceToCurrent = currentSpineIndex.map { abs(lhs.spineIndex - $0) } ?? Int.max
let rhsDistanceToCurrent = currentSpineIndex.map { abs(rhs.spineIndex - $0) } ?? Int.max
if lhsDistanceToCurrent != rhsDistanceToCurrent {
return lhsDistanceToCurrent < rhsDistanceToCurrent
}
// spineIndex
return lhs.spineIndex < rhs.spineIndex
}
return sorted.map { $0.spineIndex }
}
/// spineIndex
private func classifySpineIndex(
spineIndex: Int,
currentSpineIndex: Int?
) -> RDEPUBPriorityBand {
//
if let current = currentSpineIndex {
let distance = abs(spineIndex - current)
if distance <= policy.hotRadius {
@@ -167,7 +145,6 @@ final class RDEPUBBackgroundPriorityManager {
}
}
//
for (index, anchor) in warmAnchors.enumerated() {
let distance = abs(spineIndex - anchor.spineIndex)
if distance <= policy.warmRadius {
@@ -175,16 +152,13 @@ final class RDEPUBBackgroundPriorityManager {
}
}
//
return .cold
}
///
func currentWarmAnchors() -> [RDEPUBWarmJumpAnchor] {
warmAnchors
}
///
func reset() {
warmAnchors.removeAll()
currentGeneration = 0