This commit is contained in:
Generated
+24
@@ -44,6 +44,7 @@ dependencies = [
|
||||
"tauri-plugin-os",
|
||||
"tauri-plugin-persisted-scope",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-sharekit",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-sign-in-with-apple",
|
||||
"tauri-plugin-single-instance",
|
||||
@@ -3085,7 +3086,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.6.2",
|
||||
"dispatch2",
|
||||
"libc",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
@@ -5674,6 +5677,27 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-sharekit"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78d52e8e7f240043bb8c2cb0275feb8fb78b90ea6647a0105c043989197e69d3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.17",
|
||||
"windows",
|
||||
"windows-collections",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-shell"
|
||||
version = "2.3.3"
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.735.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.735.0",
|
||||
"@choochmeque/tauri-plugin-sharekit-api": "^0.3.0",
|
||||
"@fabianlars/tauri-plugin-oauth": "2",
|
||||
"@opennextjs/cloudflare": "^1.14.7",
|
||||
"@serwist/next": "^9.4.2",
|
||||
|
||||
@@ -876,5 +876,9 @@
|
||||
"Clear search": "مسح البحث",
|
||||
"Clear search history": "مسح سجل البحث",
|
||||
"Quick action disabled": "الإجراء السريع معطل",
|
||||
"Tap to Toggle Footer": "انقر لتبديل التذييل"
|
||||
"Tap to Toggle Footer": "انقر لتبديل التذييل",
|
||||
"Exported successfully": "تم التصدير بنجاح",
|
||||
"Book exported successfully.": "تم تصدير الكتاب بنجاح.",
|
||||
"Failed to export the book.": "فشل تصدير الكتاب.",
|
||||
"Export Book": "تصدير الكتاب"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "অনুসন্ধান সাফ করুন",
|
||||
"Clear search history": "অনুসন্ধান ইতিহাস সাফ করুন",
|
||||
"Quick action disabled": "দ্রুত কার্যক্রম অক্ষম",
|
||||
"Tap to Toggle Footer": "ফুটার টগল করতে আলতো চাপুন"
|
||||
"Tap to Toggle Footer": "ফুটার টগল করতে আলতো চাপুন",
|
||||
"Exported successfully": "সফলভাবে রপ্তানি হয়েছে",
|
||||
"Book exported successfully.": "বইটি সফলভাবে রপ্তানি হয়েছে।",
|
||||
"Failed to export the book.": "বই রপ্তানি করতে ব্যর্থ।",
|
||||
"Export Book": "বই রপ্তানি করুন"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "འཚོལ་བཤེར་གཙང་སེལ།",
|
||||
"Clear search history": "འཚོལ་བཤེར་ལོ་རྒྱུས་གཙང་སེལ།",
|
||||
"Quick action disabled": "མྱུར་མགྱོགས་བྱ་འགུལ་བཀག་འགོག",
|
||||
"Tap to Toggle Footer": "ཞབས་མཇུག་སྒོ་འབྱེད་བྱེད་པར་གནོན།"
|
||||
"Tap to Toggle Footer": "ཞབས་མཇུག་སྒོ་འབྱེད་བྱེད་པར་གནོན།",
|
||||
"Exported successfully": "ཕྱིར་འདོན་ལེགས་གྲུབ་བྱུང་།",
|
||||
"Book exported successfully.": "དཔེ་དེབ་ཕྱིར་འདོན་ལེགས་གྲུབ་བྱུང་།",
|
||||
"Failed to export the book.": "དཔེ་དེབ་ཕྱིར་འདོན་མི་ཐུབ།",
|
||||
"Export Book": "དཔེ་དེབ་ཕྱིར་འདོན།"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "Suche löschen",
|
||||
"Clear search history": "Suchverlauf löschen",
|
||||
"Quick action disabled": "Schnellaktion deaktiviert",
|
||||
"Tap to Toggle Footer": "Tippen, um Fußzeile umzuschalten"
|
||||
"Tap to Toggle Footer": "Tippen, um Fußzeile umzuschalten",
|
||||
"Exported successfully": "Erfolgreich exportiert",
|
||||
"Book exported successfully.": "Buch erfolgreich exportiert.",
|
||||
"Failed to export the book.": "Buch konnte nicht exportiert werden.",
|
||||
"Export Book": "Buch exportieren"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "Εκκαθάριση αναζήτησης",
|
||||
"Clear search history": "Εκκαθάριση ιστορικού αναζήτησης",
|
||||
"Quick action disabled": "Η γρήγορη ενέργεια απενεργοποιήθηκε",
|
||||
"Tap to Toggle Footer": "Πατήστε για εναλλαγή υποσέλιδου"
|
||||
"Tap to Toggle Footer": "Πατήστε για εναλλαγή υποσέλιδου",
|
||||
"Exported successfully": "Εξαγωγή επιτυχής",
|
||||
"Book exported successfully.": "Το βιβλίο εξήχθη επιτυχώς.",
|
||||
"Failed to export the book.": "Αποτυχία εξαγωγής του βιβλίου.",
|
||||
"Export Book": "Εξαγωγή βιβλίου"
|
||||
}
|
||||
|
||||
@@ -846,5 +846,9 @@
|
||||
"Clear search": "Borrar búsqueda",
|
||||
"Clear search history": "Borrar historial de búsqueda",
|
||||
"Quick action disabled": "Acción rápida desactivada",
|
||||
"Tap to Toggle Footer": "Toca para alternar pie de página"
|
||||
"Tap to Toggle Footer": "Toca para alternar pie de página",
|
||||
"Exported successfully": "Exportado con éxito",
|
||||
"Book exported successfully.": "Libro exportado con éxito.",
|
||||
"Failed to export the book.": "Error al exportar el libro.",
|
||||
"Export Book": "Exportar libro"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "پاک کردن جستجو",
|
||||
"Clear search history": "پاک کردن تاریخچه جستجو",
|
||||
"Quick action disabled": "عملکرد سریع غیرفعال است",
|
||||
"Tap to Toggle Footer": "برای تغییر پاورقی ضربه بزنید"
|
||||
"Tap to Toggle Footer": "برای تغییر پاورقی ضربه بزنید",
|
||||
"Exported successfully": "صادرات موفق",
|
||||
"Book exported successfully.": "کتاب با موفقیت صادر شد.",
|
||||
"Failed to export the book.": "صادر کردن کتاب ناموفق بود.",
|
||||
"Export Book": "صادر کردن کتاب"
|
||||
}
|
||||
|
||||
@@ -846,5 +846,9 @@
|
||||
"Clear search": "Effacer la recherche",
|
||||
"Clear search history": "Effacer l'historique de recherche",
|
||||
"Quick action disabled": "Action rapide désactivée",
|
||||
"Tap to Toggle Footer": "Appuyez pour afficher/masquer le pied de page"
|
||||
"Tap to Toggle Footer": "Appuyez pour afficher/masquer le pied de page",
|
||||
"Exported successfully": "Exporté avec succès",
|
||||
"Book exported successfully.": "Livre exporté avec succès.",
|
||||
"Failed to export the book.": "Échec de l'exportation du livre.",
|
||||
"Export Book": "Exporter le livre"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "खोज साफ़ करें",
|
||||
"Clear search history": "खोज इतिहास साफ़ करें",
|
||||
"Quick action disabled": "त्वरित क्रिया अक्षम",
|
||||
"Tap to Toggle Footer": "फुटर टॉगल करने के लिए टैप करें"
|
||||
"Tap to Toggle Footer": "फुटर टॉगल करने के लिए टैप करें",
|
||||
"Exported successfully": "सफलतापूर्वक निर्यात किया गया",
|
||||
"Book exported successfully.": "पुस्तक सफलतापूर्वक निर्यात की गई।",
|
||||
"Failed to export the book.": "पुस्तक निर्यात करने में विफल।",
|
||||
"Export Book": "पुस्तक निर्यात करें"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "Hapus pencarian",
|
||||
"Clear search history": "Hapus riwayat pencarian",
|
||||
"Quick action disabled": "Tindakan cepat dinonaktifkan",
|
||||
"Tap to Toggle Footer": "Ketuk untuk beralih footer"
|
||||
"Tap to Toggle Footer": "Ketuk untuk beralih footer",
|
||||
"Exported successfully": "Berhasil diekspor",
|
||||
"Book exported successfully.": "Buku berhasil diekspor.",
|
||||
"Failed to export the book.": "Gagal mengekspor buku.",
|
||||
"Export Book": "Ekspor Buku"
|
||||
}
|
||||
|
||||
@@ -846,5 +846,9 @@
|
||||
"Clear search": "Cancella ricerca",
|
||||
"Clear search history": "Cancella cronologia ricerche",
|
||||
"Quick action disabled": "Azione rapida disattivata",
|
||||
"Tap to Toggle Footer": "Tocca per mostrare/nascondere il piè di pagina"
|
||||
"Tap to Toggle Footer": "Tocca per mostrare/nascondere il piè di pagina",
|
||||
"Exported successfully": "Esportato con successo",
|
||||
"Book exported successfully.": "Libro esportato con successo.",
|
||||
"Failed to export the book.": "Impossibile esportare il libro.",
|
||||
"Export Book": "Esporta libro"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "検索をクリア",
|
||||
"Clear search history": "検索履歴をクリア",
|
||||
"Quick action disabled": "クイックアクション無効",
|
||||
"Tap to Toggle Footer": "タップでフッターを切り替え"
|
||||
"Tap to Toggle Footer": "タップでフッターを切り替え",
|
||||
"Exported successfully": "エクスポート成功",
|
||||
"Book exported successfully.": "書籍のエクスポートに成功しました。",
|
||||
"Failed to export the book.": "書籍のエクスポートに失敗しました。",
|
||||
"Export Book": "書籍をエクスポート"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "검색 지우기",
|
||||
"Clear search history": "검색 기록 지우기",
|
||||
"Quick action disabled": "빠른 작업 비활성화됨",
|
||||
"Tap to Toggle Footer": "탭하여 바닥글 전환"
|
||||
"Tap to Toggle Footer": "탭하여 바닥글 전환",
|
||||
"Exported successfully": "내보내기 성공",
|
||||
"Book exported successfully.": "책을 성공적으로 내보냈습니다.",
|
||||
"Failed to export the book.": "책 내보내기에 실패했습니다.",
|
||||
"Export Book": "책 내보내기"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "Kosongkan carian",
|
||||
"Clear search history": "Kosongkan sejarah carian",
|
||||
"Quick action disabled": "Tindakan pantas dilumpuhkan",
|
||||
"Tap to Toggle Footer": "Ketik untuk togol pengaki"
|
||||
"Tap to Toggle Footer": "Ketik untuk togol pengaki",
|
||||
"Exported successfully": "Berjaya dieksport",
|
||||
"Book exported successfully.": "Buku berjaya dieksport.",
|
||||
"Failed to export the book.": "Gagal mengeksport buku.",
|
||||
"Export Book": "Eksport Buku"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "Zoekopdracht wissen",
|
||||
"Clear search history": "Zoekgeschiedenis wissen",
|
||||
"Quick action disabled": "Snelle actie uitgeschakeld",
|
||||
"Tap to Toggle Footer": "Tik om voettekst te wisselen"
|
||||
"Tap to Toggle Footer": "Tik om voettekst te wisselen",
|
||||
"Exported successfully": "Succesvol geëxporteerd",
|
||||
"Book exported successfully.": "Boek succesvol geëxporteerd.",
|
||||
"Failed to export the book.": "Exporteren van het boek mislukt.",
|
||||
"Export Book": "Boek exporteren"
|
||||
}
|
||||
|
||||
@@ -856,5 +856,9 @@
|
||||
"Clear search": "Wyczyść wyszukiwanie",
|
||||
"Clear search history": "Wyczyść historię wyszukiwania",
|
||||
"Quick action disabled": "Szybka akcja wyłączona",
|
||||
"Tap to Toggle Footer": "Dotknij, aby przełączyć stopkę"
|
||||
"Tap to Toggle Footer": "Dotknij, aby przełączyć stopkę",
|
||||
"Exported successfully": "Wyeksportowano pomyślnie",
|
||||
"Book exported successfully.": "Książka została wyeksportowana pomyślnie.",
|
||||
"Failed to export the book.": "Nie udało się wyeksportować książki.",
|
||||
"Export Book": "Eksportuj książkę"
|
||||
}
|
||||
|
||||
@@ -846,5 +846,9 @@
|
||||
"Clear search": "Limpar pesquisa",
|
||||
"Clear search history": "Limpar histórico de pesquisa",
|
||||
"Quick action disabled": "Ação rápida desativada",
|
||||
"Tap to Toggle Footer": "Toque para alternar rodapé"
|
||||
"Tap to Toggle Footer": "Toque para alternar rodapé",
|
||||
"Exported successfully": "Exportado com sucesso",
|
||||
"Book exported successfully.": "Livro exportado com sucesso.",
|
||||
"Failed to export the book.": "Falha ao exportar o livro.",
|
||||
"Export Book": "Exportar livro"
|
||||
}
|
||||
|
||||
@@ -856,5 +856,9 @@
|
||||
"Clear search": "Очистить поиск",
|
||||
"Clear search history": "Очистить историю поиска",
|
||||
"Quick action disabled": "Быстрое действие отключено",
|
||||
"Tap to Toggle Footer": "Нажмите для переключения нижнего колонтитула"
|
||||
"Tap to Toggle Footer": "Нажмите для переключения нижнего колонтитула",
|
||||
"Exported successfully": "Успешно экспортировано",
|
||||
"Book exported successfully.": "Книга успешно экспортирована.",
|
||||
"Failed to export the book.": "Не удалось экспортировать книгу.",
|
||||
"Export Book": "Экспорт книги"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "සෙවුම හිස් කරන්න",
|
||||
"Clear search history": "සෙවුම් ඉතිහාසය හිස් කරන්න",
|
||||
"Quick action disabled": "ඉක්මන් ක්රියාව අක්රීයයි",
|
||||
"Tap to Toggle Footer": "පාදකය ටොගල් කිරීමට තට්ටු කරන්න"
|
||||
"Tap to Toggle Footer": "පාදකය ටොගල් කිරීමට තට්ටු කරන්න",
|
||||
"Exported successfully": "සාර්ථකව අපනයනය කරන ලදී",
|
||||
"Book exported successfully.": "පොත සාර්ථකව අපනයනය කරන ලදී.",
|
||||
"Failed to export the book.": "පොත අපනයනය කිරීමට අසමත් විය.",
|
||||
"Export Book": "පොත අපනයනය කරන්න"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "Rensa sökning",
|
||||
"Clear search history": "Rensa sökhistorik",
|
||||
"Quick action disabled": "Snabbåtgärd inaktiverad",
|
||||
"Tap to Toggle Footer": "Tryck för att växla sidfot"
|
||||
"Tap to Toggle Footer": "Tryck för att växla sidfot",
|
||||
"Exported successfully": "Exporterad",
|
||||
"Book exported successfully.": "Boken har exporterats.",
|
||||
"Failed to export the book.": "Det gick inte att exportera boken.",
|
||||
"Export Book": "Exportera bok"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "தேடலை அழி",
|
||||
"Clear search history": "தேடல் வரலாற்றை அழி",
|
||||
"Quick action disabled": "விரைவு செயல் முடக்கப்பட்டது",
|
||||
"Tap to Toggle Footer": "அடிக்குறிப்பை மாற்ற தட்டவும்"
|
||||
"Tap to Toggle Footer": "அடிக்குறிப்பை மாற்ற தட்டவும்",
|
||||
"Exported successfully": "ஏற்றுமதி வெற்றி",
|
||||
"Book exported successfully.": "புத்தகம் வெற்றிகரமாக ஏற்றுமதி செய்யப்பட்டது.",
|
||||
"Failed to export the book.": "புத்தகத்தை ஏற்றுமதி செய்ய இயலவில்லை.",
|
||||
"Export Book": "புத்தகத்தை ஏற்றுமதி செய்"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "ล้างการค้นหา",
|
||||
"Clear search history": "ล้างประวัติการค้นหา",
|
||||
"Quick action disabled": "ปิดใช้งานการดำเนินการด่วน",
|
||||
"Tap to Toggle Footer": "แตะเพื่อสลับส่วนท้าย"
|
||||
"Tap to Toggle Footer": "แตะเพื่อสลับส่วนท้าย",
|
||||
"Exported successfully": "ส่งออกสำเร็จ",
|
||||
"Book exported successfully.": "ส่งออกหนังสือสำเร็จ",
|
||||
"Failed to export the book.": "ส่งออกหนังสือล้มเหลว",
|
||||
"Export Book": "ส่งออกหนังสือ"
|
||||
}
|
||||
|
||||
@@ -836,5 +836,9 @@
|
||||
"Clear search": "Aramayı temizle",
|
||||
"Clear search history": "Arama geçmişini temizle",
|
||||
"Quick action disabled": "Hızlı eylem devre dışı",
|
||||
"Tap to Toggle Footer": "Altbilgiyi değiştirmek için dokunun"
|
||||
"Tap to Toggle Footer": "Altbilgiyi değiştirmek için dokunun",
|
||||
"Exported successfully": "Başarıyla dışa aktarıldı",
|
||||
"Book exported successfully.": "Kitap başarıyla dışa aktarıldı.",
|
||||
"Failed to export the book.": "Kitap dışa aktarılamadı.",
|
||||
"Export Book": "Kitabı Dışa Aktar"
|
||||
}
|
||||
|
||||
@@ -856,5 +856,9 @@
|
||||
"Clear search": "Очистити пошук",
|
||||
"Clear search history": "Очистити історію пошуку",
|
||||
"Quick action disabled": "Швидку дію вимкнено",
|
||||
"Tap to Toggle Footer": "Торкніться, щоб перемкнути нижній колонтитул"
|
||||
"Tap to Toggle Footer": "Торкніться, щоб перемкнути нижній колонтитул",
|
||||
"Exported successfully": "Успішно експортовано",
|
||||
"Book exported successfully.": "Книгу успішно експортовано.",
|
||||
"Failed to export the book.": "Не вдалося експортувати книгу.",
|
||||
"Export Book": "Експортувати книгу"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "Xóa tìm kiếm",
|
||||
"Clear search history": "Xóa lịch sử tìm kiếm",
|
||||
"Quick action disabled": "Đã tắt thao tác nhanh",
|
||||
"Tap to Toggle Footer": "Nhấn để bật/tắt chân trang"
|
||||
"Tap to Toggle Footer": "Nhấn để bật/tắt chân trang",
|
||||
"Exported successfully": "Xuất thành công",
|
||||
"Book exported successfully.": "Sách đã được xuất thành công.",
|
||||
"Failed to export the book.": "Xuất sách thất bại.",
|
||||
"Export Book": "Xuất sách"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "清除搜索",
|
||||
"Clear search history": "清除搜索历史",
|
||||
"Quick action disabled": "快捷操作已禁用",
|
||||
"Tap to Toggle Footer": "点击切换页脚"
|
||||
"Tap to Toggle Footer": "点击切换页脚",
|
||||
"Exported successfully": "导出成功",
|
||||
"Book exported successfully.": "书籍导出成功。",
|
||||
"Failed to export the book.": "书籍导出失败。",
|
||||
"Export Book": "导出书籍"
|
||||
}
|
||||
|
||||
@@ -826,5 +826,9 @@
|
||||
"Clear search": "清除搜尋",
|
||||
"Clear search history": "清除搜尋歷史",
|
||||
"Quick action disabled": "快捷操作已停用",
|
||||
"Tap to Toggle Footer": "點擊切換頁尾"
|
||||
"Tap to Toggle Footer": "點擊切換頁尾",
|
||||
"Exported successfully": "匯出成功",
|
||||
"Book exported successfully.": "書籍匯出成功。",
|
||||
"Failed to export the book.": "書籍匯出失敗。",
|
||||
"Export Book": "匯出書籍"
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ tauri-plugin-persisted-scope = "2"
|
||||
tauri-plugin-native-bridge = { path = "./plugins/tauri-plugin-native-bridge" }
|
||||
tauri-plugin-native-tts = { path = "./plugins/tauri-plugin-native-tts" }
|
||||
tauri-plugin-websocket = "2"
|
||||
tauri-plugin-sharekit = "0.3"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
rand = "0.8"
|
||||
|
||||
@@ -156,8 +156,9 @@
|
||||
"haptics:allow-impact-feedback",
|
||||
"haptics:allow-notification-feedback",
|
||||
"haptics:allow-selection-feedback",
|
||||
"native-bridge:default",
|
||||
"deep-link:default",
|
||||
"sharekit:default",
|
||||
"native-tts:default",
|
||||
"deep-link:default"
|
||||
"native-bridge:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_sharekit::init())
|
||||
.plugin(tauri_plugin_native_bridge::init())
|
||||
.plugin(tauri_plugin_native_tts::init());
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useEnv } from '@/context/EnvContext';
|
||||
import { BookNote, BooknoteGroup, HighlightColor, HighlightStyle } from '@/types/book';
|
||||
import { NOTE_PREFIX } from '@/types/view';
|
||||
import { NativeTouchEventType } from '@/types/system';
|
||||
import { getLocale, getOSPlatform, uniqueId } from '@/utils/misc';
|
||||
import { getLocale, getOSPlatform, makeSafeFilename, uniqueId } from '@/utils/misc';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
@@ -746,7 +746,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
[selection?.text],
|
||||
);
|
||||
|
||||
const handleExportMarkdown = (event: CustomEvent) => {
|
||||
const handleExportMarkdown = async (event: CustomEvent) => {
|
||||
const { bookKey: exportBookKey } = event.detail;
|
||||
if (bookKey !== exportBookKey) return;
|
||||
|
||||
@@ -813,22 +813,18 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
}
|
||||
|
||||
const markdownContent = lines.join('\n');
|
||||
setTimeout(() => {
|
||||
// Delay to ensure it won't be overridden by system clipboard actions
|
||||
navigator.clipboard?.writeText(markdownContent);
|
||||
}, 100);
|
||||
|
||||
navigator.clipboard?.writeText(markdownContent);
|
||||
const filename = `${makeSafeFilename(book.title)}.md`;
|
||||
const saved = await appService?.saveFile(filename, markdownContent, 'text/markdown');
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'info',
|
||||
message: _('Copied to clipboard'),
|
||||
className: 'whitespace-nowrap',
|
||||
message: saved ? _('Exported successfully') : _('Copied to clipboard'),
|
||||
timeout: 2000,
|
||||
});
|
||||
if (appService?.isMobile) return;
|
||||
const blob = new Blob([markdownContent], { type: 'text/markdown;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${book.title.replace(/\s+/g, '_')}.md`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const selectionAnnotated = selection?.annotated;
|
||||
|
||||
@@ -8,11 +8,14 @@ import { useThemeStore } from '@/store/themeStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useMetadataEdit } from './useMetadataEdit';
|
||||
import { DeleteAction } from '@/types/system';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { isWebAppPlatform } from '@/services/environment';
|
||||
import Alert from '@/components/Alert';
|
||||
import Dialog from '@/components/Dialog';
|
||||
import BookDetailView from './BookDetailView';
|
||||
import BookDetailEdit from './BookDetailEdit';
|
||||
import SourceSelector from './SourceSelector';
|
||||
import Spinner from '../Spinner';
|
||||
|
||||
interface BookDetailModalProps {
|
||||
book: Book;
|
||||
@@ -44,9 +47,10 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
handleBookMetadataUpdate,
|
||||
}) => {
|
||||
const _ = useTranslation();
|
||||
const { envConfig } = useEnv();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { safeAreaInsets } = useThemeStore();
|
||||
const [activeDeleteAction, setActiveDeleteAction] = useState<DeleteAction | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [bookMeta, setBookMeta] = useState<BookMetadata | null>(null);
|
||||
const [fileSize, setFileSize] = useState<number | null>(null);
|
||||
@@ -167,6 +171,20 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleBookExport = async () => {
|
||||
setIsLoading(true);
|
||||
setTimeout(async () => {
|
||||
const success = await appService?.exportBook(book);
|
||||
setIsLoading(false);
|
||||
if (!isWebAppPlatform()) {
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: success ? 'info' : 'error',
|
||||
message: success ? _('Book exported successfully.') : _('Failed to export the book.'),
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const currentDeleteConfig = activeDeleteAction ? deleteConfigs[activeDeleteAction] : null;
|
||||
|
||||
return (
|
||||
@@ -213,6 +231,7 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
onDeleteLocalCopy={handleBookDeleteLocalCopy ? handleDeleteLocalCopy : undefined}
|
||||
onDownload={handleBookDownload ? handleRedownload : undefined}
|
||||
onUpload={handleBookUpload ? handleReupload : undefined}
|
||||
onExport={handleBookExport}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -228,6 +247,12 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className='fixed inset-0 z-50 flex items-center justify-center'>
|
||||
<Spinner loading />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeDeleteAction && currentDeleteConfig && (
|
||||
<div
|
||||
className={clsx('fixed bottom-0 left-0 right-0 z-50 flex justify-center')}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
MdOutlineCloudUpload,
|
||||
MdOutlineDelete,
|
||||
MdOutlineEdit,
|
||||
MdSaveAlt,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import { Book } from '@/types/book';
|
||||
@@ -32,6 +33,7 @@ interface BookDetailViewProps {
|
||||
onDeleteLocalCopy?: () => void;
|
||||
onDownload?: () => void;
|
||||
onUpload?: () => void;
|
||||
onExport?: () => void;
|
||||
}
|
||||
|
||||
const BookDetailView: React.FC<BookDetailViewProps> = ({
|
||||
@@ -44,13 +46,14 @@ const BookDetailView: React.FC<BookDetailViewProps> = ({
|
||||
onDeleteLocalCopy,
|
||||
onDownload,
|
||||
onUpload,
|
||||
onExport,
|
||||
}) => {
|
||||
const _ = useTranslation();
|
||||
|
||||
return (
|
||||
<div className='relative w-full rounded-lg'>
|
||||
<div className='mb-6 me-4 flex h-32 items-start'>
|
||||
<div className='me-10 aspect-[28/41] h-32 shadow-lg'>
|
||||
<div className='me-6 aspect-[28/41] h-32 shadow-lg sm:me-10'>
|
||||
<BookCover mode='list' book={book} />
|
||||
</div>
|
||||
<div className='title-author flex h-32 flex-col justify-between'>
|
||||
@@ -62,7 +65,7 @@ const BookDetailView: React.FC<BookDetailViewProps> = ({
|
||||
{formatAuthors(book.author, book.primaryLanguage) || _('Unknown')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex flex-wrap items-center gap-x-4'>
|
||||
<div className='flex flex-nowrap items-center gap-3 sm:gap-x-4'>
|
||||
{onEdit && (
|
||||
<button
|
||||
onClick={onEdit}
|
||||
@@ -118,6 +121,11 @@ const BookDetailView: React.FC<BookDetailViewProps> = ({
|
||||
<MdOutlineCloudUpload className='fill-base-content' />
|
||||
</button>
|
||||
)}
|
||||
{book.downloadedAt && onExport && (
|
||||
<button onClick={onExport} title={_('Export Book')}>
|
||||
<MdSaveAlt className='fill-base-content' />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,6 +79,8 @@ export const EXTS: Record<BookFormat, string> = {
|
||||
CBZ: 'cbz',
|
||||
FB2: 'fb2',
|
||||
FBZ: 'fbz',
|
||||
TXT: 'txt',
|
||||
MD: 'md',
|
||||
};
|
||||
|
||||
export const MIMETYPES: Record<BookFormat, string[]> = {
|
||||
@@ -90,6 +92,8 @@ export const MIMETYPES: Record<BookFormat, string[]> = {
|
||||
CBZ: ['application/vnd.comicbook+zip', 'application/zip', 'application/x-cbz'],
|
||||
FB2: ['application/x-fictionbook+xml', 'text/xml', 'application/xml'],
|
||||
FBZ: ['application/x-zip-compressed-fb2', 'application/zip'],
|
||||
TXT: ['text/plain'],
|
||||
MD: ['text/markdown', 'text/x-markdown'],
|
||||
};
|
||||
|
||||
export class DocumentLoader {
|
||||
@@ -254,3 +258,14 @@ export const getFileExtFromMimeType = (mimeType?: string): string => {
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const getMimeTypeFromFileExt = (ext: string): string => {
|
||||
ext = ext.toLowerCase();
|
||||
for (const format in EXTS) {
|
||||
if (EXTS[format as BookFormat] === ext) {
|
||||
const mimeTypes = MIMETYPES[format as BookFormat];
|
||||
return mimeTypes[0] || 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
return 'application/octet-stream';
|
||||
};
|
||||
|
||||
@@ -58,7 +58,14 @@ import {
|
||||
DEFAULT_EINK_VIEW_SETTINGS,
|
||||
} from './constants';
|
||||
import { fetch as tauriFetch } from '@tauri-apps/plugin-http';
|
||||
import { getOSPlatform, getTargetLang, isCJKEnv, isContentURI, isValidURL } from '@/utils/misc';
|
||||
import {
|
||||
getOSPlatform,
|
||||
getTargetLang,
|
||||
isCJKEnv,
|
||||
isContentURI,
|
||||
isValidURL,
|
||||
makeSafeFilename,
|
||||
} from '@/utils/misc';
|
||||
import { deserializeConfig, serializeConfig } from '@/utils/serializer';
|
||||
import {
|
||||
downloadFile,
|
||||
@@ -114,6 +121,12 @@ export abstract class BaseAppService implements AppService {
|
||||
abstract setCustomRootDir(customRootDir: string): Promise<void>;
|
||||
abstract selectDirectory(mode: SelectDirectoryMode): Promise<string>;
|
||||
abstract selectFiles(name: string, extensions: string[]): Promise<string[]>;
|
||||
abstract saveFile(
|
||||
filename: string,
|
||||
content: string | ArrayBuffer,
|
||||
filepath: string,
|
||||
mimeType?: string,
|
||||
): Promise<boolean>;
|
||||
|
||||
protected async runMigrations(lastMigrationVersion: number): Promise<void> {
|
||||
if (lastMigrationVersion < 20251124) {
|
||||
@@ -661,6 +674,15 @@ export abstract class BaseAppService implements AppService {
|
||||
}
|
||||
}
|
||||
|
||||
async exportBook(book: Book): Promise<boolean> {
|
||||
const { file } = await this.loadBookContent(book);
|
||||
const content = await file.arrayBuffer();
|
||||
const filename = `${makeSafeFilename(book.title)}.${book.format.toLowerCase()}`;
|
||||
const filepath = await this.resolveFilePath(getLocalBookFilename(book), 'Books');
|
||||
const fileType = file.type || 'application/octet-stream';
|
||||
return await this.saveFile(filename, content, filepath, fileType);
|
||||
}
|
||||
|
||||
async isBookAvailable(book: Book): Promise<boolean> {
|
||||
const fp = getLocalBookFilename(book);
|
||||
if (await this.fs.exists(fp, 'Books')) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
DirEntry,
|
||||
} from '@tauri-apps/plugin-fs';
|
||||
import { invoke, convertFileSrc } from '@tauri-apps/api/core';
|
||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { open as openDialog, save as saveDialog } from '@tauri-apps/plugin-dialog';
|
||||
import {
|
||||
join,
|
||||
basename,
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
tempDir,
|
||||
} from '@tauri-apps/api/path';
|
||||
import { type as osType } from '@tauri-apps/plugin-os';
|
||||
import { shareFile } from '@choochmeque/tauri-plugin-sharekit-api';
|
||||
|
||||
import {
|
||||
FileSystem,
|
||||
@@ -474,6 +475,38 @@ export class NativeAppService extends BaseAppService {
|
||||
return Array.isArray(selected) ? selected : selected ? [selected] : [];
|
||||
}
|
||||
|
||||
async saveFile(
|
||||
filename: string,
|
||||
content: string | ArrayBuffer,
|
||||
filepath: string,
|
||||
mimeType?: string,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const ext = filename.split('.').pop() || '';
|
||||
if (this.isIOSApp) {
|
||||
await shareFile(filepath, {
|
||||
mimeType: mimeType || 'application/octet-stream',
|
||||
});
|
||||
} else {
|
||||
const filePath = await saveDialog({
|
||||
defaultPath: filename,
|
||||
filters: [{ name: ext.toUpperCase(), extensions: [ext] }],
|
||||
});
|
||||
if (!filePath) return false;
|
||||
|
||||
if (typeof content === 'string') {
|
||||
await writeTextFile(filePath, content);
|
||||
} else {
|
||||
await writeFile(filePath, new Uint8Array(content));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to save file:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async migrate20251029() {
|
||||
console.log('Running migration 20251029 to update paths in Images dir...');
|
||||
const rootPath = await this.resolveFilePath('..', 'Data');
|
||||
|
||||
@@ -321,4 +321,22 @@ export class WebAppService extends BaseAppService {
|
||||
async selectFiles(): Promise<string[]> {
|
||||
throw new Error('selectFiles is not supported in browser');
|
||||
}
|
||||
|
||||
async saveFile(filename: string, content: string | ArrayBuffer, mimeType?: string): Promise<boolean> {
|
||||
try {
|
||||
const blob = new Blob([content], { type: mimeType || 'application/octet-stream' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to save file:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,17 @@ import { BookMetadata } from '@/libs/document';
|
||||
import { TTSHighlightOptions } from '@/services/tts/types';
|
||||
import { AnnotationToolType } from './annotator';
|
||||
|
||||
export type BookFormat = 'EPUB' | 'PDF' | 'MOBI' | 'AZW' | 'AZW3' | 'CBZ' | 'FB2' | 'FBZ';
|
||||
export type BookFormat =
|
||||
| 'EPUB'
|
||||
| 'PDF'
|
||||
| 'MOBI'
|
||||
| 'AZW'
|
||||
| 'AZW3'
|
||||
| 'CBZ'
|
||||
| 'FB2'
|
||||
| 'FBZ'
|
||||
| 'TXT'
|
||||
| 'MD';
|
||||
export type BookNoteType = 'bookmark' | 'annotation' | 'excerpt';
|
||||
export type HighlightStyle = 'highlight' | 'underline' | 'squiggly';
|
||||
export type HighlightColor = 'red' | 'yellow' | 'green' | 'blue' | 'violet';
|
||||
|
||||
@@ -107,6 +107,7 @@ export interface AppService {
|
||||
selectDirectory(mode: SelectDirectoryMode): Promise<string>;
|
||||
selectFiles(name: string, extensions: string[]): Promise<string[]>;
|
||||
readDirectory(path: string, base: BaseDir): Promise<FileItem[]>;
|
||||
saveFile(filename: string, content: string | ArrayBuffer, mimeType?: string): Promise<boolean>;
|
||||
|
||||
getDefaultViewSettings(): ViewSettings;
|
||||
loadSettings(): Promise<SystemSettings>;
|
||||
@@ -132,6 +133,7 @@ export interface AppService {
|
||||
onProgress?: ProgressHandler,
|
||||
): Promise<void>;
|
||||
downloadBookCovers(books: Book[], redownload?: boolean): Promise<void>;
|
||||
exportBook(book: Book): Promise<boolean>;
|
||||
isBookAvailable(book: Book): Promise<boolean>;
|
||||
getBookFileSize(book: Book): Promise<number | null>;
|
||||
loadBookConfig(book: Book, settings: SystemSettings): Promise<BookConfig>;
|
||||
|
||||
Generated
+11
-1
@@ -52,6 +52,9 @@ importers:
|
||||
'@aws-sdk/s3-request-presigner':
|
||||
specifier: ^3.735.0
|
||||
version: 3.735.0
|
||||
'@choochmeque/tauri-plugin-sharekit-api':
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0
|
||||
'@fabianlars/tauri-plugin-oauth':
|
||||
specifier: '2'
|
||||
version: 2.0.0
|
||||
@@ -954,6 +957,9 @@ packages:
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@choochmeque/tauri-plugin-sharekit-api@0.3.0':
|
||||
resolution: {integrity: sha512-YuFbBOZIVQF2080stK4llB9l2KFKY3YuhurRVS0TeHEAiLWMbblsXuuwC4IufEM9hTChRGQyJIOnxNCn7+T/EQ==}
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.1':
|
||||
resolution: {integrity: sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -8237,6 +8243,10 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@choochmeque/tauri-plugin-sharekit-api@0.3.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.9.1
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.1':
|
||||
dependencies:
|
||||
mime: 3.0.0
|
||||
@@ -14287,7 +14297,7 @@ snapshots:
|
||||
'@webassemblyjs/wasm-edit': 1.14.1
|
||||
'@webassemblyjs/wasm-parser': 1.14.1
|
||||
acorn: 8.15.0
|
||||
browserslist: 4.24.4
|
||||
browserslist: 4.28.1
|
||||
chrome-trace-event: 1.0.4
|
||||
enhanced-resolve: 5.18.3
|
||||
es-module-lexer: 1.7.0
|
||||
|
||||
Reference in New Issue
Block a user