feat(wxread): align pagination, rendering, and docs

This commit is contained in:
shen
2026-05-24 15:36:01 +08:00
parent a318c0e3d0
commit 2a94921e88
103 changed files with 6124 additions and 4623 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "B0633BC4-5BBB-46C3-9C4F-C3B9A25989D3"
type = "0"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "7F1DAB5A-519E-47BC-A62E-B330F41132ED"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "../Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookCache.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "323"
endingLineNumber = "323">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "0313507F-BD79-4B9D-B92F-703991289849"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "../Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookCache.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "351"
endingLineNumber = "351">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
+106 -7
View File
@@ -8,6 +8,57 @@ final class ViewController: UIViewController {
let fileURL: URL
}
private struct LaunchAutomationPlan {
let bookTitleQuery: String
let displayType: RDReaderView.DisplayType?
let pageNumber: Int?
let displaySequence: [RDReaderView.DisplayType]
let stepDelay: TimeInterval
nonisolated private static func parseDisplayType(_ rawValue: String) -> RDReaderView.DisplayType? {
switch rawValue.lowercased() {
case "pagecurl", "curl":
return .pageCurl
case "horizontalscroll", "horizontal", "scroll":
return .horizontalScroll
case "verticalscroll", "vertical":
return .verticalScroll
default:
return nil
}
}
static func parse(arguments: [String]) -> LaunchAutomationPlan? {
func value(after flag: String) -> String? {
guard let index = arguments.firstIndex(of: flag), arguments.indices.contains(index + 1) else {
return nil
}
return arguments[index + 1]
}
guard let bookTitleQuery = value(after: "--demo-book-title"),
!bookTitleQuery.isEmpty else {
return nil
}
let displayType = value(after: "--demo-display-type").flatMap(parseDisplayType)
let pageNumber = value(after: "--demo-page").flatMap(Int.init)
let displaySequence = value(after: "--demo-display-sequence")
.map { raw in
raw.split(separator: ",").compactMap { parseDisplayType(String($0)) }
} ?? []
let stepDelay = value(after: "--demo-step-delay").flatMap(TimeInterval.init) ?? 1.0
return LaunchAutomationPlan(
bookTitleQuery: bookTitleQuery,
displayType: displayType,
pageNumber: pageNumber,
displaySequence: displaySequence,
stepDelay: stepDelay
)
}
}
private enum ValidationCategory: String, CaseIterable {
case textNovel = "TXT/小说"
case textRich = "复杂图文"
@@ -111,6 +162,8 @@ final class ViewController: UIViewController {
private var books: [DemoBook] = []
private var validationSummary: ResourceValidationSummary?
private var validationTask: Task<Void, Never>?
private let launchAutomationPlan = LaunchAutomationPlan.parse(arguments: ProcessInfo.processInfo.arguments)
private var didRunLaunchAutomation = false
deinit {
validationTask?.cancel()
@@ -125,6 +178,11 @@ final class ViewController: UIViewController {
loadBooks()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
runLaunchAutomationIfNeeded()
}
private func setupViews() {
tableView.dataSource = self
tableView.delegate = self
@@ -583,19 +641,40 @@ final class ViewController: UIViewController {
}
}
private func openBook(_ book: DemoBook) {
let controller = RDURLReaderController(bookURL: book.fileURL)
@discardableResult
private func openBook(
_ book: DemoBook,
configuration: RDEPUBReaderConfiguration = .default,
automationPlan: LaunchAutomationPlan? = nil
) -> RDURLReaderController {
let controller = RDURLReaderController(bookURL: book.fileURL, epubConfiguration: configuration)
controller.title = book.title
if let navigationController {
navigationController.pushViewController(controller, animated: true)
return
} else {
let hostController = UINavigationController(rootViewController: controller)
hostController.modalPresentationStyle = .fullScreen
hostController.view.accessibilityIdentifier = "demo.reader.host"
present(hostController, animated: true)
}
let hostController = UINavigationController(rootViewController: controller)
hostController.modalPresentationStyle = .fullScreen
hostController.view.accessibilityIdentifier = "demo.reader.host"
present(hostController, animated: true)
if let automationPlan {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak controller] in
if let pageNumber = automationPlan.pageNumber {
_ = controller?.goToDemoPage(pageNumber)
}
if !automationPlan.displaySequence.isEmpty {
controller?.runDemoDisplaySequence(
automationPlan.displaySequence,
initialPageNumber: nil,
stepDelay: automationPlan.stepDelay
)
}
}
}
return controller
}
private func currentReaderPageSize() -> CGSize {
@@ -619,6 +698,26 @@ final class ViewController: UIViewController {
)
}
private func runLaunchAutomationIfNeeded() {
guard !didRunLaunchAutomation,
let launchAutomationPlan,
!books.isEmpty else {
return
}
didRunLaunchAutomation = true
guard let book = books.first(where: { $0.title.localizedCaseInsensitiveContains(launchAutomationPlan.bookTitleQuery) }) else {
print("[ReadViewDemo] automation skipped: missing book matching \(launchAutomationPlan.bookTitleQuery)")
return
}
var configuration = RDEPUBReaderConfiguration.default
if let displayType = launchAutomationPlan.displayType {
configuration.displayType = displayType
}
_ = openBook(book, configuration: configuration, automationPlan: launchAutomationPlan)
}
}
extension ViewController: UITableViewDataSource {