// RDEPUBSearchModels.swift // EPUB 搜索相关数据模型 // 定义搜索匹配项(RDEPUBSearchMatch)、搜索结果(RDEPUBSearchResult)、 // 搜索状态(RDEPUBSearchState)和搜索展示(RDEPUBSearchPresentation)等数据结构。 import Foundation /// 搜索匹配项,包含匹配位置、进度、预览文本和范围信息 public struct RDEPUBSearchMatch: Codable, Equatable { /// 匹配所在资源的规范化 href public var href: String /// 匹配位置在文本中的进度比例(0.0 ~ 1.0) public var progression: Double /// 匹配位置前后的预览文本 public var previewText: String /// 在当前资源内的本地匹配索引 public var localMatchIndex: Int /// 匹配范围的起始位置(字符偏移量) public var rangeLocation: Int? /// 匹配范围的长度 public var rangeLength: Int /// 文本范围锚点(用于精确定位) public var rangeAnchor: RDEPUBTextRangeAnchor? public init( href: String, progression: Double, previewText: String, localMatchIndex: Int, rangeLocation: Int? = nil, rangeLength: Int, rangeAnchor: RDEPUBTextRangeAnchor? = nil ) { self.href = href self.progression = progression self.previewText = previewText self.localMatchIndex = localMatchIndex self.rangeLocation = rangeLocation self.rangeLength = rangeLength self.rangeAnchor = rangeAnchor } } /// 搜索结果摘要,包含关键词、总匹配数和当前匹配项 public struct RDEPUBSearchResult: Codable, Equatable { /// 搜索关键词 public var keyword: String /// 总匹配数量 public var totalMatchCount: Int /// 当前匹配项的索引(从 1 开始) public var currentMatchIndex: Int? /// 当前高亮的匹配项 public var currentMatch: RDEPUBSearchMatch? public init( keyword: String, totalMatchCount: Int, currentMatchIndex: Int?, currentMatch: RDEPUBSearchMatch? ) { self.keyword = keyword self.totalMatchCount = totalMatchCount self.currentMatchIndex = currentMatchIndex self.currentMatch = currentMatch } } /// 搜索状态,管理关键词、匹配列表和当前索引 public struct RDEPUBSearchState: Codable, Equatable { /// 搜索关键词 public var keyword: String /// 所有匹配项列表 public var matches: [RDEPUBSearchMatch] /// 当前匹配项索引(从 0 开始) public var currentMatchIndex: Int? public init(keyword: String, matches: [RDEPUBSearchMatch], currentMatchIndex: Int? = nil) { self.keyword = keyword self.matches = matches self.currentMatchIndex = currentMatchIndex } /// 获取当前匹配项 public var currentMatch: RDEPUBSearchMatch? { guard let currentMatchIndex, matches.indices.contains(currentMatchIndex) else { return nil } return matches[currentMatchIndex] } /// 转换为搜索结果摘要 public var result: RDEPUBSearchResult { RDEPUBSearchResult( keyword: keyword, totalMatchCount: matches.count, currentMatchIndex: currentMatchIndex.map { $0 + 1 }, currentMatch: currentMatch ) } } /// 搜索展示的单个资源信息 public struct RDEPUBSearchPresentationResource: Codable, Equatable { /// 资源的规范化 href public var href: String /// 该资源内的匹配数量 public var matchCount: Int /// 当前活跃的本地匹配索引 public var activeLocalMatchIndex: Int? public init(href: String, matchCount: Int, activeLocalMatchIndex: Int? = nil) { self.href = href self.matchCount = matchCount self.activeLocalMatchIndex = activeLocalMatchIndex } } /// 搜索展示信息,传递给 JS 桥接脚本用于渲染搜索高亮 public struct RDEPUBSearchPresentation: Codable, Equatable { /// 搜索关键词 public var keyword: String /// 各资源的搜索展示信息列表 public var resources: [RDEPUBSearchPresentationResource] public init(keyword: String, resources: [RDEPUBSearchPresentationResource]) { self.keyword = keyword self.resources = resources } }