feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
This commit is contained in:
@@ -1,26 +1,35 @@
|
||||
import Foundation
|
||||
|
||||
public struct RDEPUBLocation: Codable, Equatable {
|
||||
/// 书籍唯一标识符,用于隔离不同书的阅读进度
|
||||
|
||||
public var bookIdentifier: String?
|
||||
/// OPF 相对路径,对应 spine 中的资源文件
|
||||
|
||||
public var href: String
|
||||
/// 视口起始位置在章节中的相对进度 [0, 1]
|
||||
|
||||
public var progression: Double
|
||||
/// 视口末尾位置在章节中的相对进度 [0, 1](可选)
|
||||
|
||||
public var lastProgression: Double?
|
||||
/// 锚点标识符(如 #section1),用于精确跳转
|
||||
|
||||
public var fragment: String?
|
||||
/// 文本范围锚点(用于文本 EPUB 的高亮定位)
|
||||
|
||||
public var rangeAnchor: RDEPUBTextRangeAnchor?
|
||||
|
||||
public var cfi: String?
|
||||
|
||||
public var lastCFI: String?
|
||||
|
||||
public var rangeCFI: String?
|
||||
|
||||
public init(
|
||||
bookIdentifier: String? = nil,
|
||||
href: String,
|
||||
progression: Double,
|
||||
lastProgression: Double? = nil,
|
||||
fragment: String? = nil,
|
||||
rangeAnchor: RDEPUBTextRangeAnchor? = nil
|
||||
rangeAnchor: RDEPUBTextRangeAnchor? = nil,
|
||||
cfi: String? = nil,
|
||||
lastCFI: String? = nil,
|
||||
rangeCFI: String? = nil
|
||||
) {
|
||||
self.bookIdentifier = bookIdentifier
|
||||
self.href = href
|
||||
@@ -28,10 +37,11 @@ public struct RDEPUBLocation: Codable, Equatable {
|
||||
self.lastProgression = lastProgression.map(Self.clamp)
|
||||
self.fragment = fragment?.nilIfEmpty
|
||||
self.rangeAnchor = rangeAnchor
|
||||
self.cfi = cfi?.nilIfEmpty
|
||||
self.lastCFI = lastCFI?.nilIfEmpty
|
||||
self.rangeCFI = rangeCFI?.nilIfEmpty
|
||||
}
|
||||
|
||||
/// 导航用的中间进度值,取 progression 和 lastProgression 的中点
|
||||
/// 用于在字号/横竖屏变化后估算最接近的页号
|
||||
public var navigationProgression: Double {
|
||||
let end = lastProgression ?? progression
|
||||
if end.isNaN || end.isInfinite {
|
||||
@@ -40,24 +50,22 @@ public struct RDEPUBLocation: Codable, Equatable {
|
||||
return Self.clamp((progression + end) / 2.0)
|
||||
}
|
||||
|
||||
/// 将进度值限制在 [0, 1] 范围内,防止越界
|
||||
private static func clamp(_ value: Double) -> Double {
|
||||
guard value.isFinite else { return 0 }
|
||||
return min(1, max(0, value))
|
||||
}
|
||||
}
|
||||
|
||||
/// 视口中的单个资源信息,表示当前屏幕可见区域覆盖的某个 spine 资源
|
||||
public struct RDEPUBViewportResource: Codable, Equatable {
|
||||
/// 资源路径(相对于 OPF 目录)
|
||||
|
||||
public var href: String
|
||||
/// 对应的 spine 索引
|
||||
|
||||
public var spineIndex: Int
|
||||
/// 该资源在视口中的起始进度 [0, 1]
|
||||
|
||||
public var progression: Double
|
||||
/// 该资源在视口中的结束进度 [0, 1]
|
||||
|
||||
public var lastProgression: Double?
|
||||
/// 锚点标识符
|
||||
|
||||
public var fragment: String?
|
||||
|
||||
public init(
|
||||
@@ -75,16 +83,14 @@ public struct RDEPUBViewportResource: Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
/// 视口模型,描述当前屏幕可见区域的完整状态
|
||||
/// 可能跨越多个 spine 资源(如分栏布局时)
|
||||
public struct RDEPUBViewport: Codable, Equatable {
|
||||
/// 当前视口覆盖的资源列表(可能有多个,如分栏或跨页场景)
|
||||
|
||||
public var resources: [RDEPUBViewportResource]
|
||||
/// 当前可见的页码
|
||||
|
||||
public var visiblePageNumber: Int
|
||||
/// 当前章节索引
|
||||
|
||||
public var chapterIndex: Int?
|
||||
/// 是否为固定版式内容
|
||||
|
||||
public var isFixedLayout: Bool
|
||||
|
||||
public init(
|
||||
@@ -100,16 +106,14 @@ public struct RDEPUBViewport: Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
/// 阅读上下文,聚合了位置、视口和页码等完整阅读状态
|
||||
/// 由 RDEPUBReadingSession 维护,是翻页容器层获取当前阅读状态的主要数据源
|
||||
public struct RDEPUBReadingContext: Codable, Equatable {
|
||||
/// 当前阅读位置(href + progression)
|
||||
|
||||
public var location: RDEPUBLocation
|
||||
/// 当前视口状态
|
||||
|
||||
public var viewport: RDEPUBViewport
|
||||
/// 当前扁平页码(跨章节累计)
|
||||
|
||||
public var pageNumber: Int
|
||||
/// 当前章节索引
|
||||
|
||||
public var chapterIndex: Int?
|
||||
|
||||
public init(
|
||||
@@ -125,8 +129,6 @@ public struct RDEPUBReadingContext: Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
/// 文本选择模型,由 JS 桥接 ssReaderSelectionChanged 消息触发创建
|
||||
|
||||
private extension String {
|
||||
var nilIfEmpty: String? {
|
||||
let trimmed = trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
Reference in New Issue
Block a user