feat: 支持 EPUB 阅读并改善规则兼容与阅读稳定性

支持 EPUB 2/3 导入、目录、封面和章节插图,保留本地书管理与阅读进度。
修复 JSON 模板、动态替换与请求参数兼容,以及 RSS 翻页和连续滚动定位。
更新编辑器工具栏和回归测试,发布版本为 0.1.12(build 18)。
This commit is contained in:
Aminark
2026-10-24 00:58:11 +08:00
parent 3322fd71db
commit 67bc212ffd
58 changed files with 2331 additions and 244 deletions
+11 -10
View File
@@ -207,7 +207,7 @@ struct ReadingPhotoPickerController: UIViewControllerRepresentable {
}
}
private struct ReadingTXTFileImporterModifier: ViewModifier {
private struct ReadingLocalBookFileImporterModifier: ViewModifier {
@Binding var isPresented: Bool
let onFiles: ([LocalBookImportFile]) -> Void
let onError: (Error) -> Void
@@ -219,7 +219,7 @@ private struct ReadingTXTFileImporterModifier: ViewModifier {
pickerHasDismissed = true
deliverFilesWhenReady()
}) {
ReadingTXTFilePicker { files in
ReadingLocalBookFilePicker { files in
pickedFiles = files
isPresented = false
deliverFilesWhenReady()
@@ -239,7 +239,7 @@ private struct ReadingTXTFileImporterModifier: ViewModifier {
guard pickerHasDismissed, !pickedFiles.isEmpty else { return }
let files = pickedFiles
pickedFiles = []
guard files.allSatisfy({ $0.url.pathExtension.lowercased() == "txt" && !$0.url.hasDirectoryPath }) else {
guard files.allSatisfy({ LocalBookService.supports($0.url) && !$0.url.hasDirectoryPath }) else {
onError(LocalBookError.unsupportedFormat)
return
}
@@ -247,15 +247,16 @@ private struct ReadingTXTFileImporterModifier: ViewModifier {
}
}
private struct ReadingTXTFilePicker: UIViewControllerRepresentable {
private struct ReadingLocalBookFilePicker: UIViewControllerRepresentable {
let onPick: @MainActor ([LocalBookImportFile]) -> Void
let onCancel: @MainActor () -> Void
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
// Plain text includes UTF-8/UTF-16 TXT, while .text and .data also allow JSON.
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.plainText], asCopy: true)
// Keep source JSON out of the local-book picker.
let epub = UTType(filenameExtension: "epub") ?? UTType(importedAs: "org.idpf.epub-container")
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.plainText, epub], asCopy: true)
picker.allowsMultipleSelection = true
picker.shouldShowFileExtensions = true
picker.delegate = context.coordinator
@@ -266,9 +267,9 @@ private struct ReadingTXTFilePicker: UIViewControllerRepresentable {
@MainActor
final class Coordinator: NSObject, UIDocumentPickerDelegate {
let parent: ReadingTXTFilePicker
let parent: ReadingLocalBookFilePicker
init(parent: ReadingTXTFilePicker) { self.parent = parent }
init(parent: ReadingLocalBookFilePicker) { self.parent = parent }
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { parent.onCancel() }
@@ -281,12 +282,12 @@ private struct ReadingTXTFilePicker: UIViewControllerRepresentable {
enum ReadingSheetDetent { case medium, large }
extension View {
func readingTXTFileImporter(
func readingLocalBookFileImporter(
isPresented: Binding<Bool>,
onFiles: @escaping ([LocalBookImportFile]) -> Void,
onError: @escaping (Error) -> Void
) -> some View {
modifier(ReadingTXTFileImporterModifier(isPresented: isPresented, onFiles: onFiles, onError: onError))
modifier(ReadingLocalBookFileImporterModifier(isPresented: isPresented, onFiles: onFiles, onError: onError))
}
func readingPhotoPicker(