Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2ef55832c | |||
| b879184dc6 | |||
| 8bd9e18381 | |||
| 4121195fd3 | |||
| cebe8812d3 | |||
| 0a13f36e33 | |||
| ded6053134 | |||
| 1c6cc2bca0 | |||
| 6ca96be917 | |||
| a057af7881 | |||
| 279504395d | |||
| cfd5d5d106 | |||
| 93eacf91a0 | |||
| dc6cbd2029 | |||
| 643fdd24a9 | |||
| 5c55233c5c | |||
| 4d7db3592f |
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libsoup-3.0-dev
|
||||
sudo apt-get install -y pkg-config libfontconfig-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libsoup-3.0-dev
|
||||
- name: Format
|
||||
working-directory: apps/readest-app/src-tauri
|
||||
run: cargo fmt --check
|
||||
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
if: matrix.config.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
sudo apt-get install -y pkg-config libfontconfig-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[![Website][badge-website]][link-website]
|
||||
[![Web App][badge-web-app]][link-web-readest]
|
||||
[![OS][badge-platforms]][link-website]
|
||||
[![][badge-hellogithub]][link-hellogithub]
|
||||
[![][badge-discord]][link-discord]
|
||||
<br>
|
||||
[![AGPL Licence][badge-license]](LICENSE)
|
||||
@@ -39,20 +40,20 @@
|
||||
|
||||
<div align="left">✅ Implemented</div>
|
||||
|
||||
| **Feature** | **Description** | **Status** |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------- | ---------- |
|
||||
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental) | ✅ |
|
||||
| **Scroll/Page View Modes** | Switch between scrolling or paginated reading modes. | ✅ |
|
||||
| **Full-Text Search** | Search across the entire book to find relevant sections. | ✅ |
|
||||
| **Annotations and Highlighting** | Add highlights, bookmarks, and notes to enhance your reading experience. | ✅ |
|
||||
| **Excerpt Text for Note-Taking** | Easily excerpt text from books for detailed notes and analysis. | ✅ |
|
||||
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
|
||||
| **Translate with DeepL** | Translate selected text instantly using DeepL for accurate translations. | ✅ |
|
||||
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
|
||||
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
|
||||
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
|
||||
| **Sync across Platforms** | Synchronize reading progress, notes, and bookmarks across all supported platforms. | ✅ |
|
||||
| **Text-to-Speech (TTS) Support** | Enable text-to-speech functionality for a more accessible reading experience. | ✅ |
|
||||
| **Feature** | **Description** | **Status** |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
||||
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental) | ✅ |
|
||||
| **Scroll/Page View Modes** | Switch between scrolling or paginated reading modes. | ✅ |
|
||||
| **Full-Text Search** | Search across the entire book to find relevant sections. | ✅ |
|
||||
| **Annotations and Highlighting** | Add highlights, bookmarks, and notes to enhance your reading experience. | ✅ |
|
||||
| **Excerpt Text for Note-Taking** | Easily excerpt text from books for detailed notes and analysis. | ✅ |
|
||||
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
|
||||
| **Translate with DeepL** | Translate selected text instantly using DeepL for accurate translations. | ✅ |
|
||||
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
|
||||
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
|
||||
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
|
||||
| **Sync across Platforms** | Synchronize book files, reading progress, notes, and bookmarks across all supported platforms. | ✅ |
|
||||
| **Text-to-Speech (TTS) Support** | Enable text-to-speech functionality for a more accessible reading experience. | ✅ |
|
||||
|
||||
## Planned Features
|
||||
|
||||
@@ -193,7 +194,7 @@ Readest is open-source, and contributions are welcome! Feel free to open issues,
|
||||
|
||||
Readest is free software: you can redistribute it and/or modify it under the terms of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
The following libraries and software are used in this software:
|
||||
The following libraries and frameworks are used in this software:
|
||||
|
||||
- [foliate-js](https://github.com/johnfactotum/foliate-js), which is MIT licensed.
|
||||
- [zip.js](https://github.com/gildas-lormeau/zip.js), which is licensed under the BSD-3-Clause license.
|
||||
@@ -220,6 +221,7 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[badge-last-commit]: https://img.shields.io/github/last-commit/readest/readest?color=green
|
||||
[badge-commit-activity]: https://img.shields.io/github/commit-activity/m/readest/readest
|
||||
[badge-discord]: https://img.shields.io/discord/1314226120886976544?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square
|
||||
[badge-hellogithub]: https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=8a5b6ade2aee461a8bd94e59200682a7&claim_uid=eRLUbPOy2qZtDgw&theme=small
|
||||
[link-macos-appstore]: https://apps.apple.com/app/apple-store/id6738622779?pt=127463130&ct=github&mt=8
|
||||
[link-website]: https://readest.com?utm_source=github&utm_medium=referral&utm_campaign=readme
|
||||
[link-web-readest]: https://web.readest.com
|
||||
@@ -230,3 +232,4 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[link-discord]: https://discord.gg/gntyVNk3BJ
|
||||
[link-parallel-read]: https://readest.com/#parallel-read
|
||||
[link-koreader]: https://github.com/koreader/koreader
|
||||
[link-hellogithub]: https://hellogithub.com/repository/8a5b6ade2aee461a8bd94e59200682a7
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@readest/readest-app",
|
||||
"version": "0.9.7",
|
||||
"version": "0.9.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "dotenv -e .env.tauri -- next dev",
|
||||
@@ -36,18 +36,18 @@
|
||||
"@supabase/auth-ui-react": "^0.4.7",
|
||||
"@supabase/auth-ui-shared": "^0.1.8",
|
||||
"@supabase/supabase-js": "^2.47.7",
|
||||
"@tauri-apps/api": "2.1.1",
|
||||
"@tauri-apps/api": "2.2.0",
|
||||
"@tauri-apps/plugin-cli": "^2.2.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.0",
|
||||
"@tauri-apps/plugin-fs": "^2.2.0",
|
||||
"@tauri-apps/plugin-http": "^2.2.0",
|
||||
"@tauri-apps/plugin-log": "^2.2.0",
|
||||
"@tauri-apps/plugin-opener": "^2.2.2",
|
||||
"@tauri-apps/plugin-http": "^2.3.0",
|
||||
"@tauri-apps/plugin-log": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2.2.5",
|
||||
"@tauri-apps/plugin-os": "^2.2.0",
|
||||
"@tauri-apps/plugin-process": "^2.2.0",
|
||||
"@tauri-apps/plugin-shell": "~2.2.0",
|
||||
"@tauri-apps/plugin-updater": "^2.3.0",
|
||||
"@tauri-apps/plugin-updater": "^2.4.0",
|
||||
"@zip.js/zip.js": "^2.7.53",
|
||||
"clsx": "^2.1.1",
|
||||
"cors": "^2.8.5",
|
||||
@@ -58,7 +58,7 @@
|
||||
"i18next-http-backend": "^3.0.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"next": "15.1.0",
|
||||
"next": "15.1.6",
|
||||
"posthog-js": "^1.205.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
@@ -69,7 +69,7 @@
|
||||
"zustand": "5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "2.1.0",
|
||||
"@tauri-apps/cli": "2.2.7",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/cssbeautify": "^0.3.5",
|
||||
"@types/node": "^22.10.1",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"applinks": {
|
||||
"details": [
|
||||
{
|
||||
"appIDs": [
|
||||
"J5W48D69VR.com.bilingify.readest"
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"/": "/auth/*",
|
||||
"comment": "Matches any URL whose path starts with /auth/"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.bilingify.readest",
|
||||
"sha256_cert_fingerprints": [
|
||||
"65:2D:11:67:76:12:29:14:18:42:CB:3D:18:50:B6:E4:7E:46:E1:2F:4B:E4:7F:5A:6C:14:B6:D7:12:74:1E:82"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Speicher",
|
||||
"Book deleted: {{title}}": "Buch gelöscht: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Fehler beim Löschen des Buches: {{title}}",
|
||||
"Check Updates on Start": "Aktualisierungen beim Start prüfen"
|
||||
"Check Updates on Start": "Aktualisierungen beim Start prüfen",
|
||||
"Insufficient storage quota": "Unzureichendes Speicherkontingent"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Αποθήκευση",
|
||||
"Book deleted: {{title}}": "Το βιβλίο με τίτλο {{title}} διαγράφηκε",
|
||||
"Failed to delete book: {{title}}": "Αποτυχία διαγραφής βιβλίου: {{title}}",
|
||||
"Check Updates on Start": "Έλεγχος ενημερώσεων κατά την εκκίνηση"
|
||||
"Check Updates on Start": "Έλεγχος ενημερώσεων κατά την εκκίνηση",
|
||||
"Insufficient storage quota": "Ανεπαρκές όριο αποθήκευσης"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Almacenamiento",
|
||||
"Book deleted: {{title}}": "Libro eliminado: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Error al eliminar libro: {{title}}",
|
||||
"Check Updates on Start": "Comprobar actualizaciones al iniciar"
|
||||
"Check Updates on Start": "Comprobar actualizaciones al iniciar",
|
||||
"Insufficient storage quota": "Cuota de almacenamiento insuficiente"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Stockage",
|
||||
"Book deleted: {{title}}": "Livre supprimé : {{title}}",
|
||||
"Failed to delete book: {{title}}": "Échec de la suppression du livre : {{title}}",
|
||||
"Check Updates on Start": "Vérifier les mises à jour au démarrage"
|
||||
"Check Updates on Start": "Vérifier les mises à jour au démarrage",
|
||||
"Insufficient storage quota": "Quota de stockage insuffisant"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "स्टोरेज",
|
||||
"Book deleted: {{title}}": "पुस्तक हटाई गई: {{title}}",
|
||||
"Failed to delete book: {{title}}": "पुस्तक हटाने में विफल: {{title}}",
|
||||
"Check Updates on Start": "शुरू में अपडेट जांचें"
|
||||
"Check Updates on Start": "शुरू में अपडेट जांचें",
|
||||
"Insufficient storage quota": "अपर्याप्त स्टोरेज कोटा"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Penyimpanan",
|
||||
"Book deleted: {{title}}": "Buku dihapus: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Gagal menghapus buku: {{title}}",
|
||||
"Check Updates on Start": "Periksa Pembaruan saat Memulai"
|
||||
"Check Updates on Start": "Periksa Pembaruan saat Memulai",
|
||||
"Insufficient storage quota": "Kuota penyimpanan tidak mencukupi"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Archiviazione",
|
||||
"Book deleted: {{title}}": "Libro eliminato: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Eliminazione libro non riuscita: {{title}}",
|
||||
"Check Updates on Start": "Controlla aggiornamenti all'avvio"
|
||||
"Check Updates on Start": "Controlla aggiornamenti all'avvio",
|
||||
"Insufficient storage quota": "Quota di archiviazione insufficiente"
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"Select Books": "書籍を選択",
|
||||
"Select Multiple Books": "複数の書籍を選択",
|
||||
"Sepia": "セピア",
|
||||
"Serif Font": "明朝体",
|
||||
"Serif Font": "セリフ体",
|
||||
"Show Book Details": "書籍の詳細を表示",
|
||||
"Sidebar": "サイドバー",
|
||||
"Sign In": "サインイン",
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "ストレージ",
|
||||
"Book deleted: {{title}}": "書籍が削除されました:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍の削除に失敗しました:{{title}}",
|
||||
"Check Updates on Start": "開始時に更新を確認"
|
||||
"Check Updates on Start": "開始時に更新を確認",
|
||||
"Insufficient storage quota": "ストレージクォータが不足しています"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "저장소",
|
||||
"Book deleted: {{title}}": "책 삭제됨: {{title}}",
|
||||
"Failed to delete book: {{title}}": "책 삭제 실패: {{title}}",
|
||||
"Check Updates on Start": "시작 시 업데이트 확인"
|
||||
"Check Updates on Start": "시작 시 업데이트 확인",
|
||||
"Insufficient storage quota": "저장소 할당량 부족"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Przechowywanie",
|
||||
"Book deleted: {{title}}": "Książka usunięta: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Nie udało się usunąć książki: {{title}}",
|
||||
"Check Updates on Start": "Sprawdź aktualizacje przy starcie"
|
||||
"Check Updates on Start": "Sprawdź aktualizacje przy starcie",
|
||||
"Insufficient storage quota": "Niewystarczająca kwota przechowywania"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Armazenamento",
|
||||
"Book deleted: {{title}}": "Livro excluído: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Falha ao excluir livro: {{title}}",
|
||||
"Check Updates on Start": "Verificar Atualizações ao Iniciar"
|
||||
"Check Updates on Start": "Verificar Atualizações ao Iniciar",
|
||||
"Insufficient storage quota": "Cota de armazenamento insuficiente"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Хранилище",
|
||||
"Book deleted: {{title}}": "Книга удалена: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Не удалось удалить книгу: {{title}}",
|
||||
"Check Updates on Start": "Проверять обновления при запуске"
|
||||
"Check Updates on Start": "Проверять обновления при запуске",
|
||||
"Insufficient storage quota": "Недостаточно квоты хранилища"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Depolama",
|
||||
"Book deleted: {{title}}": "Kitap silindi: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Kitap silinemedi: {{title}}",
|
||||
"Check Updates on Start": "Başlangıçta Güncellemeleri Kontrol Et"
|
||||
"Check Updates on Start": "Başlangıçta Güncellemeleri Kontrol Et",
|
||||
"Insufficient storage quota": "Yetersiz depolama kotası"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Сховище",
|
||||
"Book deleted: {{title}}": "Книгу видалено: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Не вдалося видалити книгу: {{title}}",
|
||||
"Check Updates on Start": "Перевіряти оновлення при запуску"
|
||||
"Check Updates on Start": "Перевіряти оновлення при запуску",
|
||||
"Insufficient storage quota": "Недостатньо квоти сховища"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "Lưu trữ",
|
||||
"Book deleted: {{title}}": "Sách đã xóa: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Không thể xóa sách: {{title}}",
|
||||
"Check Updates on Start": "Kiểm tra cập nhật khi khởi động"
|
||||
"Check Updates on Start": "Kiểm tra cập nhật khi khởi động",
|
||||
"Insufficient storage quota": "Không đủ lượng lưu trữ"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "存储空间",
|
||||
"Book deleted: {{title}}": "书籍已删除:{{title}}",
|
||||
"Failed to delete book: {{title}}": "删除书籍失败:{{title}}",
|
||||
"Check Updates on Start": "启动时检查更新"
|
||||
"Check Updates on Start": "启动时检查更新",
|
||||
"Insufficient storage quota": "云存储空间不足"
|
||||
}
|
||||
|
||||
@@ -149,5 +149,6 @@
|
||||
"Storage": "存儲空間",
|
||||
"Book deleted: {{title}}": "書籍已刪除:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍刪除失敗:{{title}}",
|
||||
"Check Updates on Start": "啟動時檢查更新"
|
||||
"Check Updates on Start": "啟動時檢查更新",
|
||||
"Insufficient storage quota": "雲存儲空間不足"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
{
|
||||
"releases": {
|
||||
"0.9.9": {
|
||||
"date": "2025-02-06",
|
||||
"notes": [
|
||||
"Load system fonts list at runtime on desktop",
|
||||
"Incrementally load new books in synchronization",
|
||||
"Change voice language dynamically by detecting text language",
|
||||
"Various fixes and enhancements on sync, fonts and highlighting"
|
||||
]
|
||||
},
|
||||
"0.9.8": {
|
||||
"date": "2025-02-01",
|
||||
"notes": [
|
||||
"Support cloud sync for book files",
|
||||
"Use larger font size on mobile devices for better reading experience",
|
||||
"Various fixes and enhancements on sync, rendering and settings"
|
||||
]
|
||||
},
|
||||
"0.9.7": {
|
||||
"date": "2025-01-23",
|
||||
"notes": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pnpm run build-macos-universial-appstore
|
||||
|
||||
BUNDLE_DIR=src-tauri/target/universal-apple-darwin/release/bundle/macos
|
||||
BUNDLE_DIR=../../target/universal-apple-darwin/release/bundle/macos
|
||||
APP_BUNDLE=$BUNDLE_DIR/Readest.app
|
||||
INSTALLER_BUNDLE=$BUNDLE_DIR/Readest.pkg
|
||||
|
||||
|
||||
@@ -57,3 +57,4 @@ tauri-plugin-cli = "2"
|
||||
tauri-plugin-single-instance = "2.2.0"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-window-state = "2.2.0"
|
||||
font-enumeration = "0.9.0"
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
@@ -72,6 +72,17 @@ async fn start_server(window: Window) -> Result<u16, String> {
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
#[command]
|
||||
async fn list_fonts() -> Result<Vec<String>, String> {
|
||||
let font_collection = font_enumeration::Collection::new().unwrap();
|
||||
let mut fonts = Vec::new();
|
||||
for font in font_collection.all() {
|
||||
fonts.push(font.font_name.clone());
|
||||
}
|
||||
Ok(fonts)
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
struct Payload {
|
||||
args: Vec<String>,
|
||||
@@ -86,7 +97,9 @@ pub fn run() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
start_server,
|
||||
download_file,
|
||||
upload_file
|
||||
upload_file,
|
||||
#[cfg(desktop)]
|
||||
list_fonts
|
||||
])
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
"section": "text"
|
||||
}
|
||||
},
|
||||
"android": {
|
||||
"minSdkVersion": 26
|
||||
},
|
||||
"fileAssociations": [
|
||||
{
|
||||
"name": "epub",
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Auth } from '@supabase/auth-ui-react';
|
||||
import { ThemeSupa } from '@supabase/auth-ui-shared';
|
||||
import { FcGoogle } from 'react-icons/fc';
|
||||
import { FaApple } from 'react-icons/fa';
|
||||
import { VscAzure } from 'react-icons/vsc';
|
||||
import { FaGithub } from 'react-icons/fa';
|
||||
import { IoArrowBack } from 'react-icons/io5';
|
||||
|
||||
@@ -17,10 +16,12 @@ import { useTheme } from '@/hooks/useTheme';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
|
||||
import { onOpenUrl, getCurrent } from '@tauri-apps/plugin-deep-link';
|
||||
import { start, cancel, onUrl, onInvalidUrl } from '@fabianlars/tauri-plugin-oauth';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import { tauriHandleOnWindowFocus } from '@/utils/window';
|
||||
import { handleAuthCallback } from '@/helpers/auth';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
|
||||
type OAuthProvider = 'google' | 'apple' | 'azure' | 'github';
|
||||
|
||||
@@ -60,8 +61,17 @@ export default function AuthPage() {
|
||||
const [port, setPort] = useState<number | null>(null);
|
||||
const isOAuthServerRunning = useRef(false);
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const osPlatform = getOSPlatform();
|
||||
|
||||
const signIn = async (provider: OAuthProvider) => {
|
||||
const getTauriRedirectTo = () => {
|
||||
return process.env.NODE_ENV === 'production'
|
||||
? ['android', 'ios'].includes(osPlatform)
|
||||
? 'https://web.readest.com/auth/callback'
|
||||
: 'readest://auth/callback'
|
||||
: `http://localhost:${port}`;
|
||||
};
|
||||
|
||||
const tauriSignIn = async (provider: OAuthProvider) => {
|
||||
if (!supabase) {
|
||||
throw new Error('No backend connected');
|
||||
}
|
||||
@@ -70,10 +80,7 @@ export default function AuthPage() {
|
||||
provider,
|
||||
options: {
|
||||
skipBrowserRedirect: true,
|
||||
redirectTo:
|
||||
process.env.NODE_ENV === 'production'
|
||||
? 'readest://auth/callback'
|
||||
: `http://localhost:${port}`,
|
||||
redirectTo: getTauriRedirectTo(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,6 +89,21 @@ export default function AuthPage() {
|
||||
return;
|
||||
}
|
||||
openUrl(data.url);
|
||||
|
||||
// FIXME: For Android we need a better way to trigger the deeplink redirect
|
||||
if (isTauriAppPlatform() && osPlatform === 'android') {
|
||||
tauriHandleOnWindowFocus(async () => {
|
||||
const urls = await getCurrent();
|
||||
if (urls && urls.length > 0) {
|
||||
urls.forEach((url) => {
|
||||
handleOAuthUrl(url);
|
||||
});
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.back();
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOAuthUrl = async (url: string) => {
|
||||
@@ -200,25 +222,19 @@ export default function AuthPage() {
|
||||
<div style={{ maxWidth: '420px', margin: 'auto', padding: '2rem' }}>
|
||||
<ProviderLogin
|
||||
provider='google'
|
||||
handleSignIn={signIn}
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FcGoogle}
|
||||
label='Sign in with Google'
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='apple'
|
||||
handleSignIn={signIn}
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FaApple}
|
||||
label='Sign in with Apple'
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='azure'
|
||||
handleSignIn={signIn}
|
||||
Icon={VscAzure}
|
||||
label='Sign in with Azure'
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='github'
|
||||
handleSignIn={signIn}
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FaGithub}
|
||||
label='Sign in with GitHub'
|
||||
/>
|
||||
@@ -229,7 +245,7 @@ export default function AuthPage() {
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
magicLink={true}
|
||||
providers={[]}
|
||||
redirectTo={`http://localhost:${port}`}
|
||||
redirectTo={getTauriRedirectTo()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -246,7 +262,7 @@ export default function AuthPage() {
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
magicLink={true}
|
||||
providers={['google', 'apple', 'azure', 'github']}
|
||||
providers={['google', 'apple', 'github']}
|
||||
redirectTo='/auth/callback'
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CiCircleMore } from 'react-icons/ci';
|
||||
import { LiaCloudUploadAltSolid, LiaCloudDownloadAltSolid } from 'react-icons/lia';
|
||||
|
||||
import { Book } from '@/types/book';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import ReadingProgress from './ReadingProgress';
|
||||
|
||||
@@ -34,10 +35,11 @@ const BookItem: React.FC<BookItemProps> = ({
|
||||
bookContextMenuHandler,
|
||||
}) => {
|
||||
const iconSize15 = useResponsiveSize(15);
|
||||
const { appService } = useEnv();
|
||||
|
||||
return (
|
||||
<div
|
||||
className='book-item cursor-pointer'
|
||||
className={clsx('book-item', appService?.hasContextMenu ? 'cursor-pointer' : '')}
|
||||
onContextMenu={bookContextMenuHandler.bind(null, book)}
|
||||
>
|
||||
<div className='bg-base-100 shadow-md' onClick={() => handleBookClick(book)}>
|
||||
|
||||
@@ -15,7 +15,6 @@ import { navigateToReader } from '@/utils/nav';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
import { getFilename } from '@/utils/book';
|
||||
import { FILE_REVEAL_LABELS, FILE_REVEAL_PLATFORMS } from '@/utils/os';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
|
||||
import Alert from '@/components/Alert';
|
||||
import Spinner from '@/components/Spinner';
|
||||
@@ -174,7 +173,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
};
|
||||
|
||||
const bookContextMenuHandler = async (book: Book, e: React.MouseEvent) => {
|
||||
if (!isTauriAppPlatform()) return;
|
||||
if (!appService?.hasContextMenu) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const osPlatform = getOSPlatform();
|
||||
|
||||
@@ -43,6 +43,8 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
};
|
||||
}, [onToggleSelectMode]);
|
||||
|
||||
const windowButtonVisible = appService?.appPlatform !== 'web' && !isTrafficLightVisible;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={headerRef}
|
||||
@@ -104,14 +106,12 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
>
|
||||
<SettingsMenu />
|
||||
</Dropdown>
|
||||
{!isTrafficLightVisible && appService?.appPlatform !== 'web' && (
|
||||
<WindowButtons
|
||||
headerRef={headerRef}
|
||||
showMinimize={true}
|
||||
showMaximize={true}
|
||||
showClose={true}
|
||||
/>
|
||||
)}
|
||||
<WindowButtons
|
||||
headerRef={headerRef}
|
||||
showMinimize={windowButtonVisible}
|
||||
showMaximize={windowButtonVisible}
|
||||
showClose={windowButtonVisible}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,12 @@ import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { SYNC_BOOKS_INTERVAL_SEC } from '@/services/constants';
|
||||
import { Book } from '@/types/book';
|
||||
|
||||
export const useBooksSync = () => {
|
||||
export interface UseBooksSyncProps {
|
||||
onSyncStart?: () => void;
|
||||
onSyncEnd?: () => void;
|
||||
}
|
||||
|
||||
export const useBooksSync = ({ onSyncStart, onSyncEnd }: UseBooksSyncProps) => {
|
||||
const { user } = useAuth();
|
||||
const { appService } = useEnv();
|
||||
const { library, setLibrary } = useLibraryStore();
|
||||
@@ -91,16 +96,23 @@ export const useBooksSync = () => {
|
||||
if (!updatedLibrary.some((oldBook) => oldBook.hash === newBook.hash)) {
|
||||
if (newBook.uploadedAt && !newBook.deletedAt) {
|
||||
try {
|
||||
updatedLibrary.push(newBook);
|
||||
await appService?.downloadBook(newBook, true);
|
||||
newBook.coverImageUrl = await appService?.generateCoverImageUrl(newBook);
|
||||
updatedLibrary.push(newBook);
|
||||
setLibrary(updatedLibrary);
|
||||
} catch {
|
||||
console.error('Failed to download book:', newBook);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
await Promise.all(syncedBooks.map(processNewBook));
|
||||
onSyncStart?.();
|
||||
const batchSize = 3;
|
||||
for (let i = 0; i < syncedBooks.length; i += batchSize) {
|
||||
const batch = syncedBooks.slice(i, i + batchSize);
|
||||
await Promise.all(batch.map(processNewBook));
|
||||
}
|
||||
onSyncEnd?.();
|
||||
setLibrary(updatedLibrary);
|
||||
appService?.saveLibraryBooks(updatedLibrary);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Book } from '@/types/book';
|
||||
import { AppService } from '@/types/system';
|
||||
import { navigateToReader } from '@/utils/nav';
|
||||
import { navigateToLogin, navigateToReader } from '@/utils/nav';
|
||||
import { getBaseFilename, listFormater } from '@/utils/book';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { parseOpenWithFiles } from '@/helpers/cli';
|
||||
@@ -57,7 +57,10 @@ const LibraryPage = () => {
|
||||
const demoBooks = useDemoBooks();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { pullLibrary, pushLibrary } = useBooksSync();
|
||||
const { pullLibrary, pushLibrary } = useBooksSync({
|
||||
onSyncStart: () => setLoading(true),
|
||||
onSyncEnd: () => setLoading(false),
|
||||
});
|
||||
|
||||
usePullToRefresh(containerRef, pullLibrary);
|
||||
|
||||
@@ -244,7 +247,19 @@ const LibraryPage = () => {
|
||||
title: book.title,
|
||||
}),
|
||||
});
|
||||
} catch {
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
if (err.message.includes('Not authenticated')) {
|
||||
navigateToLogin(router);
|
||||
return;
|
||||
} else if (err.message.includes('Insufficient storage quota')) {
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'error',
|
||||
message: _('Insufficient storage quota'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'error',
|
||||
message: _('Failed to upload book: {{title}}', {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useFoliateEvents } from '../hooks/useFoliateEvents';
|
||||
import { useProgressSync } from '../hooks/useProgressSync';
|
||||
import { useAutoHideScrollbar } from '../hooks/useAutoHideScrollbar';
|
||||
import { getStyles, mountAdditionalFonts } from '@/utils/style';
|
||||
import { getBookDirFromWritingMode } from '@/utils/book';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
|
||||
import {
|
||||
@@ -86,7 +87,8 @@ const FoliateViewer: React.FC<{
|
||||
if (renderer) {
|
||||
if (renderer.start <= 0) {
|
||||
viewRef.current?.prev(1);
|
||||
} else if (renderer.end >= renderer.viewSize) {
|
||||
// sometimes viewSize has subpixel value that the end never reaches
|
||||
} else if (renderer.end + 1 >= renderer.viewSize) {
|
||||
viewRef.current?.next(1);
|
||||
}
|
||||
}
|
||||
@@ -140,6 +142,11 @@ const FoliateViewer: React.FC<{
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
view.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
|
||||
const writingMode = viewSettings.writingMode;
|
||||
if (writingMode) {
|
||||
view.book.dir = getBookDirFromWritingMode(writingMode);
|
||||
}
|
||||
|
||||
const isScrolled = viewSettings.scrolled!;
|
||||
const marginPx = viewSettings.marginPx!;
|
||||
const gapPercent = viewSettings.gapPercent!;
|
||||
|
||||
@@ -5,7 +5,12 @@ import { BiMoon, BiSun } from 'react-icons/bi';
|
||||
import { TbSunMoon } from 'react-icons/tb';
|
||||
import { MdZoomOut, MdZoomIn, MdCheck } from 'react-icons/md';
|
||||
|
||||
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
|
||||
import {
|
||||
MAX_ZOOM_LEVEL,
|
||||
MIN_ZOOM_LEVEL,
|
||||
ONE_COLUMN_MAX_INLINE_SIZE,
|
||||
ZOOM_STEP,
|
||||
} from '@/services/constants';
|
||||
import MenuItem from '@/components/MenuItem';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
@@ -32,8 +37,8 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
const [isInvertedColors, setInvertedColors] = useState(viewSettings!.invert);
|
||||
const [zoomLevel, setZoomLevel] = useState(viewSettings!.zoomLevel!);
|
||||
|
||||
const zoomIn = () => setZoomLevel((prev) => Math.min(prev + 1, MAX_ZOOM_LEVEL));
|
||||
const zoomOut = () => setZoomLevel((prev) => Math.max(prev - 1, MIN_ZOOM_LEVEL));
|
||||
const zoomIn = () => setZoomLevel((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM_LEVEL));
|
||||
const zoomOut = () => setZoomLevel((prev) => Math.max(prev - ZOOM_STEP, MIN_ZOOM_LEVEL));
|
||||
const resetZoom = () => setZoomLevel(100);
|
||||
const toggleScrolledMode = () => setScrolledMode(!isScrolledMode);
|
||||
const toggleInvertedColors = () => setInvertedColors(!isInvertedColors);
|
||||
@@ -104,7 +109,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
)}
|
||||
onClick={resetZoom}
|
||||
>
|
||||
{100 - (100 - zoomLevel) * 10}%
|
||||
{zoomLevel}%
|
||||
</button>
|
||||
<button
|
||||
onClick={zoomIn}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useDefaultIconSize, useResponsiveSize } from '@/hooks/useResponsiveSize
|
||||
interface DropdownProps {
|
||||
family?: string;
|
||||
selected: string;
|
||||
options: string[];
|
||||
options: { option: string; label: string }[];
|
||||
moreOptions?: string[];
|
||||
onSelect: (option: string) => void;
|
||||
onGetFontFamily: (option: string, family: string) => string;
|
||||
@@ -25,26 +25,31 @@ const FontDropdown: React.FC<DropdownProps> = ({
|
||||
const _ = useTranslation();
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
const defaultIconSize = useDefaultIconSize();
|
||||
const selectedOption = options.find((option) => option.option === selected)!;
|
||||
return (
|
||||
<div className='dropdown dropdown-top'>
|
||||
<button
|
||||
tabIndex={0}
|
||||
className='btn btn-sm flex items-center gap-1 px-[20px] font-normal normal-case'
|
||||
>
|
||||
<span style={{ fontFamily: onGetFontFamily(selected, family ?? '') }}>{selected}</span>
|
||||
<span style={{ fontFamily: onGetFontFamily(selectedOption.option, family ?? '') }}>
|
||||
{selectedOption.label}
|
||||
</span>
|
||||
<FiChevronUp size={iconSize16} />
|
||||
</button>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className='dropdown-content bgcolor-base-200 no-triangle menu rounded-box absolute right-[-32px] z-[1] mt-4 w-44 shadow sm:right-0'
|
||||
>
|
||||
{options.map((option) => (
|
||||
{options.map(({ option, label }) => (
|
||||
<li key={option} onClick={() => onSelect(option)}>
|
||||
<div className='flex items-center px-0'>
|
||||
<span style={{ minWidth: `${defaultIconSize}px` }}>
|
||||
{selected === option && <MdCheck className='text-base-content' />}
|
||||
</span>
|
||||
<span style={{ fontFamily: onGetFontFamily(option, family ?? '') }}>{option}</span>
|
||||
<span style={{ fontFamily: onGetFontFamily(option, family ?? '') }}>
|
||||
{label || option}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -19,6 +19,8 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
import { FONT_ENUM_SUPPORTED_OS_PLATFORMS, getSysFontsList } from '@/utils/font';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
|
||||
interface FontFaceProps {
|
||||
className?: string;
|
||||
@@ -30,8 +32,6 @@ interface FontFaceProps {
|
||||
onSelect: (option: string) => void;
|
||||
}
|
||||
|
||||
const fontFamilyOptions = ['Serif', 'Sans-serif'];
|
||||
|
||||
const handleFontFaceFont = (option: string, family: string) => {
|
||||
return `'${option}', ${family}`;
|
||||
};
|
||||
@@ -49,7 +49,7 @@ const FontFace = ({
|
||||
<span className=''>{label}</span>
|
||||
<FontDropdown
|
||||
family={family}
|
||||
options={options}
|
||||
options={options.map((option) => ({ option, label: option }))}
|
||||
moreOptions={moreOptions}
|
||||
selected={selected}
|
||||
onSelect={onSelect}
|
||||
@@ -66,27 +66,39 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const fontFamilyOptions = [
|
||||
{
|
||||
option: 'Serif',
|
||||
label: _('Serif Font'),
|
||||
},
|
||||
{
|
||||
option: 'Sans-serif',
|
||||
label: _('Sans-Serif Font'),
|
||||
},
|
||||
];
|
||||
|
||||
const osPlatform = getOSPlatform();
|
||||
let moreFonts: string[] = [];
|
||||
let defaultSysFonts: string[] = [];
|
||||
switch (osPlatform) {
|
||||
case 'macos':
|
||||
moreFonts = MACOS_FONTS;
|
||||
defaultSysFonts = MACOS_FONTS;
|
||||
break;
|
||||
case 'windows':
|
||||
moreFonts = WINDOWS_FONTS;
|
||||
defaultSysFonts = WINDOWS_FONTS;
|
||||
break;
|
||||
case 'linux':
|
||||
moreFonts = LINUX_FONTS;
|
||||
defaultSysFonts = LINUX_FONTS;
|
||||
break;
|
||||
case 'ios':
|
||||
moreFonts = IOS_FONTS;
|
||||
defaultSysFonts = IOS_FONTS;
|
||||
break;
|
||||
case 'android':
|
||||
moreFonts = ANDROID_FONTS;
|
||||
defaultSysFonts = ANDROID_FONTS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const [sysFonts, setSysFonts] = useState<string[]>(defaultSysFonts);
|
||||
const [defaultFontSize, setDefaultFontSize] = useState(viewSettings.defaultFontSize!);
|
||||
const [minFontSize, setMinFontSize] = useState(viewSettings.minimumFontSize!);
|
||||
const [overrideFont, setOverrideFont] = useState(viewSettings.overrideFont!);
|
||||
@@ -95,6 +107,15 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont!);
|
||||
const [monospaceFont, setMonospaceFont] = useState(viewSettings.monospaceFont!);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTauriAppPlatform() && FONT_ENUM_SUPPORTED_OS_PLATFORMS.includes(osPlatform)) {
|
||||
getSysFontsList().then((fonts) => {
|
||||
setSysFonts(fonts);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.defaultFont = defaultFont;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
@@ -247,7 +268,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
family='serif'
|
||||
label={_('Serif Font')}
|
||||
options={SERIF_FONTS}
|
||||
moreOptions={moreFonts}
|
||||
moreOptions={sysFonts}
|
||||
selected={serifFont}
|
||||
onSelect={setSerifFont}
|
||||
/>
|
||||
@@ -255,7 +276,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
family='sans-serif'
|
||||
label={_('Sans-Serif Font')}
|
||||
options={SANS_SERIF_FONTS}
|
||||
moreOptions={moreFonts}
|
||||
moreOptions={sysFonts}
|
||||
selected={sansSerifFont}
|
||||
onSelect={setSansSerifFont}
|
||||
/>
|
||||
@@ -264,7 +285,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
family='monospace'
|
||||
label={_('Monospace Font')}
|
||||
options={MONOSPACE_FONTS}
|
||||
moreOptions={moreFonts}
|
||||
moreOptions={sysFonts}
|
||||
selected={monospaceFont}
|
||||
onSelect={setMonospaceFont}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { getBookLangCode } from '@/utils/book';
|
||||
import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book';
|
||||
import NumberInput from './NumberInput';
|
||||
|
||||
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
@@ -134,7 +134,10 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// global settings are not supported for writing mode
|
||||
viewSettings.writingMode = writingMode;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
if (view) {
|
||||
view.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
view.book.dir = getBookDirFromWritingMode(writingMode);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [writingMode]);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '@/services/constants';
|
||||
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ZOOM_STEP } from '@/services/constants';
|
||||
|
||||
interface UseBookShortcutsProps {
|
||||
sideBarBookKey: string | null;
|
||||
@@ -83,7 +83,7 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) =
|
||||
const view = getView(sideBarBookKey);
|
||||
if (!view?.renderer?.setStyles) return;
|
||||
const viewSettings = getViewSettings(sideBarBookKey)!;
|
||||
const zoomLevel = viewSettings!.zoomLevel + 1;
|
||||
const zoomLevel = viewSettings!.zoomLevel + ZOOM_STEP;
|
||||
viewSettings!.zoomLevel = Math.min(zoomLevel, MAX_ZOOM_LEVEL);
|
||||
setViewSettings(sideBarBookKey, viewSettings!);
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings!, themeCode));
|
||||
@@ -94,7 +94,7 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) =
|
||||
const view = getView(sideBarBookKey);
|
||||
if (!view?.renderer?.setStyles) return;
|
||||
const viewSettings = getViewSettings(sideBarBookKey)!;
|
||||
const zoomLevel = viewSettings!.zoomLevel - 1;
|
||||
const zoomLevel = viewSettings!.zoomLevel - ZOOM_STEP;
|
||||
viewSettings!.zoomLevel = Math.max(zoomLevel, MIN_ZOOM_LEVEL);
|
||||
setViewSettings(sideBarBookKey, viewSettings!);
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings!, themeCode));
|
||||
|
||||
@@ -63,6 +63,7 @@ export interface BookDoc {
|
||||
subject?: string[];
|
||||
identifier?: string;
|
||||
};
|
||||
dir: string;
|
||||
toc?: Array<TOCItem>;
|
||||
sections?: Array<SectionItem>;
|
||||
splitTOCHref(href: string): Array<string | number>;
|
||||
|
||||
@@ -82,6 +82,9 @@ export const uploadFile = async (
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('File upload failed:', error);
|
||||
if (error instanceof Error) {
|
||||
throw error;
|
||||
}
|
||||
throw new Error('File upload failed');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ export abstract class BaseAppService implements AppService {
|
||||
abstract isAppDataSandbox: boolean;
|
||||
abstract hasTrafficLight: boolean;
|
||||
abstract hasWindowBar: boolean;
|
||||
abstract hasContextMenu: boolean;
|
||||
|
||||
abstract resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string };
|
||||
abstract getCoverImageUrl(book: Book): string;
|
||||
@@ -319,7 +320,8 @@ export abstract class BaseAppService implements AppService {
|
||||
let file: File;
|
||||
const fp = getFilename(book);
|
||||
if (await this.fs.exists(fp, 'Books')) {
|
||||
if (this.appPlatform === 'web') {
|
||||
// TODO: fix random access for android
|
||||
if (this.appPlatform === 'web' || getOSPlatform() === 'android') {
|
||||
const content = await this.fs.readFile(fp, 'Books', 'binary');
|
||||
file = new File([content], fp);
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const FILE_ACCEPT_FORMATS = SUPPORTED_FILE_EXTS.map((ext) => `.${ext}`).j
|
||||
|
||||
export const DEFAULT_SYSTEM_SETTINGS: Partial<SystemSettings> = {
|
||||
keepLogin: false,
|
||||
autoUpload: false,
|
||||
autoUpload: true,
|
||||
autoCheckUpdates: true,
|
||||
|
||||
lastSyncedAtBooks: 0,
|
||||
@@ -175,13 +175,10 @@ export const WINDOWS_FONTS = [
|
||||
'SimSun-ExtB',
|
||||
'Sitka',
|
||||
'Sylfaen',
|
||||
'Symbol',
|
||||
'Tahoma',
|
||||
'Times New Roman',
|
||||
'Trebuchet MS',
|
||||
'Verdana',
|
||||
'Webdings',
|
||||
'Wingdings',
|
||||
'XiHeiti',
|
||||
'Yu Gothic',
|
||||
'Yu Mincho',
|
||||
@@ -309,11 +306,9 @@ export const LINUX_FONTS = [
|
||||
'Sazanami Mincho',
|
||||
'Source Han Sans',
|
||||
'Source Han Serif',
|
||||
'Symbola',
|
||||
'Times New Roman',
|
||||
'Ubuntu',
|
||||
'Ubuntu Mono',
|
||||
'Wingdings',
|
||||
'WenQuanYi Micro Hei',
|
||||
'WenQuanYi Zen Hei',
|
||||
'XiHeiti',
|
||||
@@ -384,8 +379,9 @@ export const SYNC_NOTES_INTERVAL_SEC = 10;
|
||||
export const SYNC_BOOKS_INTERVAL_SEC = 10;
|
||||
export const CHECK_UPDATE_INTERVAL_SEC = 24 * 60 * 60;
|
||||
|
||||
export const MAX_ZOOM_LEVEL = 140;
|
||||
export const MIN_ZOOM_LEVEL = 95;
|
||||
export const MAX_ZOOM_LEVEL = 500;
|
||||
export const MIN_ZOOM_LEVEL = 50;
|
||||
export const ZOOM_STEP = 10;
|
||||
|
||||
export const DEFAULT_STORAGE_QUOTA: UserStorageQuota = {
|
||||
free: 500 * 1024 * 1024,
|
||||
|
||||
@@ -120,6 +120,7 @@ export class NativeAppService extends BaseAppService {
|
||||
isAppDataSandbox = isMobile;
|
||||
hasTrafficLight = osType() === 'macos';
|
||||
hasWindowBar = !(osType() === 'ios' || osType() === 'android');
|
||||
hasContextMenu = !(osType() === 'ios' || osType() === 'android');
|
||||
|
||||
override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } {
|
||||
return resolvePath(fp, base);
|
||||
|
||||
@@ -8,6 +8,7 @@ export class EdgeTTSClient implements TTSClient {
|
||||
#rate = 1.0;
|
||||
#pitch = 1.0;
|
||||
#voice: TTSVoice | null = null;
|
||||
#currentVoiceLang = '';
|
||||
#voices: TTSVoice[] = [];
|
||||
#edgeTTS: EdgeSpeechTTS;
|
||||
|
||||
@@ -60,12 +61,17 @@ export class EdgeTTSClient implements TTSClient {
|
||||
preload = false,
|
||||
): AsyncGenerator<TTSMessageEvent> {
|
||||
const { marks } = parseSSMLMarks(ssml);
|
||||
const lang = parseSSMLLang(ssml) || 'en';
|
||||
const ssmlLang = parseSSMLLang(ssml) || 'en';
|
||||
let lang = ssmlLang;
|
||||
if (lang === 'en' && /[\p{Script=Han}]/u.test(ssml)) {
|
||||
lang = 'zh';
|
||||
}
|
||||
|
||||
let voiceId = 'en-US-AriaNeural';
|
||||
if (!this.#voice) {
|
||||
if (!this.#voice || ssmlLang !== lang) {
|
||||
const voices = await this.getVoices(lang);
|
||||
this.#voice = voices[0] ? voices[0] : this.#voices.find((v) => v.id === voiceId) || null;
|
||||
this.#currentVoiceLang = lang;
|
||||
}
|
||||
if (this.#voice) {
|
||||
voiceId = this.#voice.id;
|
||||
@@ -229,6 +235,9 @@ export class EdgeTTSClient implements TTSClient {
|
||||
}
|
||||
|
||||
async getVoices(lang: string): Promise<TTSVoice[]> {
|
||||
if (this.#currentVoiceLang) {
|
||||
lang = this.#currentVoiceLang;
|
||||
}
|
||||
const locale = lang === 'en' ? getUserLocale(lang) || lang : lang;
|
||||
const voices = await this.getAllVoices();
|
||||
return voices.filter((v) => v.lang.startsWith(locale));
|
||||
|
||||
@@ -175,6 +175,7 @@ export class WebSpeechClient implements TTSClient {
|
||||
#rate = 1.0;
|
||||
#pitch = 1.0;
|
||||
#voice: SpeechSynthesisVoice | null = null;
|
||||
#currentVoiceLang = '';
|
||||
#voices: SpeechSynthesisVoice[] = [];
|
||||
#synth = window.speechSynthesis;
|
||||
available = true;
|
||||
@@ -213,11 +214,16 @@ export class WebSpeechClient implements TTSClient {
|
||||
// no need to preload for web speech
|
||||
if (preload) return;
|
||||
|
||||
const lang = parseSSMLLang(ssml) || 'en';
|
||||
if (!this.#voice) {
|
||||
const ssmlLang = parseSSMLLang(ssml) || 'en';
|
||||
let lang = ssmlLang;
|
||||
if (lang === 'en' && /[\p{Script=Han}]/u.test(ssml)) {
|
||||
lang = 'zh';
|
||||
}
|
||||
if (!this.#voice || ssmlLang !== lang) {
|
||||
const voices = await this.getVoices(lang);
|
||||
const voiceId = voices[0]?.id ?? '';
|
||||
this.#voice = this.#voices.find((v) => v.voiceURI === voiceId) || null;
|
||||
this.#currentVoiceLang = lang;
|
||||
}
|
||||
for await (const ev of speakWithMarks(
|
||||
ssml,
|
||||
@@ -286,6 +292,9 @@ export class WebSpeechClient implements TTSClient {
|
||||
}
|
||||
|
||||
async getVoices(lang: string) {
|
||||
if (this.#currentVoiceLang) {
|
||||
lang = this.#currentVoiceLang;
|
||||
}
|
||||
const locale = lang === 'en' ? getUserLocale(lang) || lang : lang;
|
||||
const isValidVoice = (id: string) => {
|
||||
return !id.includes('com.apple') || id.includes('com.apple.voice.compact');
|
||||
|
||||
@@ -181,6 +181,7 @@ export class WebAppService extends BaseAppService {
|
||||
isAppDataSandbox = false;
|
||||
hasTrafficLight = false;
|
||||
hasWindowBar = false;
|
||||
hasContextMenu = false;
|
||||
|
||||
override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } {
|
||||
return resolvePath(fp, base);
|
||||
|
||||
@@ -46,6 +46,8 @@ export interface BookNote {
|
||||
deletedAt?: number | null;
|
||||
}
|
||||
|
||||
export type WritingMode = 'auto' | 'horizontal-tb' | 'horizontal-rl' | 'vertical-rl';
|
||||
|
||||
export interface BookLayout {
|
||||
marginPx: number;
|
||||
gapPercent: number;
|
||||
@@ -55,7 +57,7 @@ export interface BookLayout {
|
||||
maxInlineSize: number;
|
||||
maxBlockSize: number;
|
||||
animated: boolean;
|
||||
writingMode: string;
|
||||
writingMode: WritingMode;
|
||||
vertical: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface AppService {
|
||||
appPlatform: AppPlatform;
|
||||
hasTrafficLight: boolean;
|
||||
hasWindowBar: boolean;
|
||||
hasContextMenu: boolean;
|
||||
isMobile: boolean;
|
||||
isAppDataSandbox: boolean;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface FoliateView extends HTMLElement {
|
||||
select: (target: string | number | { fraction: number }) => void;
|
||||
deselect: () => void;
|
||||
initTTS: (granularity?: TTSGranularity) => Promise<void>;
|
||||
book: BookDoc;
|
||||
tts: TTS | null;
|
||||
language: {
|
||||
locale?: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EXTS } from '@/libs/document';
|
||||
import { Book, BookConfig, BookProgress } from '@/types/book';
|
||||
import { Book, BookConfig, BookProgress, WritingMode } from '@/types/book';
|
||||
import { getUserLang, makeSafeFilename } from './misc';
|
||||
|
||||
export const getDir = (book: Book) => {
|
||||
@@ -119,3 +119,15 @@ export const getCurrentPage = (book: Book, progress: BookProgress) => {
|
||||
? pageinfo.current + 1
|
||||
: 0;
|
||||
};
|
||||
|
||||
export const getBookDirFromWritingMode = (writingMode: WritingMode) => {
|
||||
switch (writingMode) {
|
||||
case 'horizontal-tb':
|
||||
return 'ltr';
|
||||
case 'horizontal-rl':
|
||||
case 'vertical-rl':
|
||||
return 'rtl';
|
||||
default:
|
||||
return 'auto';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getOSPlatform } from './misc';
|
||||
|
||||
class RemoteBlobSlice extends Blob {
|
||||
#dataPromise: Promise<ArrayBuffer>;
|
||||
#type: string;
|
||||
@@ -83,7 +85,7 @@ export class RemoteFile extends File {
|
||||
return this.#lastModified;
|
||||
}
|
||||
|
||||
async open() {
|
||||
async _open_with_head() {
|
||||
const response = await fetch(this.url, { method: 'HEAD' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch file size: ${response.status}`);
|
||||
@@ -93,6 +95,25 @@ export class RemoteFile extends File {
|
||||
return this;
|
||||
}
|
||||
|
||||
async _open_with_range() {
|
||||
const response = await fetch(this.url, { headers: { Range: `bytes=${0}-${1023}` } });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch file size: ${response.status}`);
|
||||
}
|
||||
this.#size = Number(response.headers.get('content-range')?.split('/')[1]);
|
||||
this.#type = response.headers.get('content-type') || '';
|
||||
return this;
|
||||
}
|
||||
|
||||
async open() {
|
||||
// FIXME: currently HEAD request in asset protocol is not supported on Android
|
||||
if (getOSPlatform() === 'android') {
|
||||
return this._open_with_range();
|
||||
} else {
|
||||
return this._open_with_head();
|
||||
}
|
||||
}
|
||||
|
||||
async fetchRangePart(start: number, end: number) {
|
||||
start = Math.max(0, start);
|
||||
end = Math.min(this.size - 1, end);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getOSPlatform } from './misc';
|
||||
|
||||
let cachedSysFonts: string[] | null = null;
|
||||
|
||||
export const FONT_ENUM_SUPPORTED_OS_PLATFORMS = ['macos', 'windows', 'linux'];
|
||||
|
||||
const isSymbolicFontName = (font: string) =>
|
||||
/emoji|icons|symbol|dingbats|ornaments|webdings|wingdings/i.test(font);
|
||||
|
||||
export const getSysFontsList = async (): Promise<string[]> => {
|
||||
if (cachedSysFonts) {
|
||||
return cachedSysFonts;
|
||||
}
|
||||
|
||||
try {
|
||||
const osPlatform = getOSPlatform();
|
||||
if (FONT_ENUM_SUPPORTED_OS_PLATFORMS.includes(osPlatform)) {
|
||||
const fonts = await invoke<string[]>('list_fonts');
|
||||
cachedSysFonts = fonts.filter((font) => !isSymbolicFontName(font)).sort();
|
||||
console.log('Fetched font list:', cachedSysFonts);
|
||||
return cachedSysFonts;
|
||||
} else {
|
||||
console.warn(`Unsupported platform: ${osPlatform}`);
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching font list:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
@@ -10,7 +10,8 @@ export const makeSafeFilename = (filename: string, replacement = '_') => {
|
||||
// Windows restricted characters + control characters and reserved names
|
||||
const unsafeCharacters = /[<>:"\/\\|?*\x00-\x1F]/g;
|
||||
const reservedFilenames = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
|
||||
const maxFilenameLength = 255;
|
||||
// Unsafe to use filename including file extensions over 255 bytes on Android
|
||||
const maxFilenameBytes = 250;
|
||||
|
||||
let safeName = filename.replace(unsafeCharacters, replacement);
|
||||
|
||||
@@ -18,8 +19,12 @@ export const makeSafeFilename = (filename: string, replacement = '_') => {
|
||||
safeName = `${safeName}${replacement}`;
|
||||
}
|
||||
|
||||
if (safeName.length > maxFilenameLength) {
|
||||
safeName = safeName.substring(0, maxFilenameLength);
|
||||
const encoder = new TextEncoder();
|
||||
let utf8Bytes = encoder.encode(safeName);
|
||||
|
||||
while (utf8Bytes.length > maxFilenameBytes) {
|
||||
safeName = safeName.slice(0, -1);
|
||||
utf8Bytes = encoder.encode(safeName);
|
||||
}
|
||||
|
||||
return safeName.trim();
|
||||
|
||||
@@ -153,7 +153,7 @@ const getLayoutStyles = (
|
||||
${bg === '#ffffff' ? '' : `color: inherit;`}
|
||||
${bg === '#ffffff' ? '' : `background-color: ${bg} !important;`}
|
||||
}
|
||||
body * {
|
||||
body {
|
||||
zoom: ${zoomLevel};
|
||||
}
|
||||
svg, img {
|
||||
|
||||
@@ -24,3 +24,12 @@ export const tauriHandleOnCloseWindow = async (callback: () => void) => {
|
||||
await exit(0);
|
||||
});
|
||||
};
|
||||
|
||||
export const tauriHandleOnWindowFocus = async (callback: () => void) => {
|
||||
const { TauriEvent } = await import('@tauri-apps/api/event');
|
||||
const { getCurrentWindow } = await import('@tauri-apps/api/window');
|
||||
const currentWindow = getCurrentWindow();
|
||||
return currentWindow.listen(TauriEvent.WINDOW_FOCUS, async () => {
|
||||
await callback();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ importers:
|
||||
version: 3.735.0
|
||||
'@ducanh2912/next-pwa':
|
||||
specifier: ^10.2.9
|
||||
version: 10.2.9(next@15.1.0(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(webpack@5.97.1)
|
||||
version: 10.2.9(next@15.1.6(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(webpack@5.97.1)
|
||||
'@fabianlars/tauri-plugin-oauth':
|
||||
specifier: '2'
|
||||
version: 2.0.0
|
||||
@@ -54,8 +54,8 @@ importers:
|
||||
specifier: ^2.47.7
|
||||
version: 2.47.7
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
'@tauri-apps/plugin-cli':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
@@ -69,14 +69,14 @@ importers:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
'@tauri-apps/plugin-http':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
'@tauri-apps/plugin-log':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
'@tauri-apps/plugin-opener':
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2
|
||||
specifier: ^2.2.5
|
||||
version: 2.2.5
|
||||
'@tauri-apps/plugin-os':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
@@ -87,8 +87,8 @@ importers:
|
||||
specifier: ~2.2.0
|
||||
version: 2.2.0
|
||||
'@tauri-apps/plugin-updater':
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
'@zip.js/zip.js':
|
||||
specifier: ^2.7.53
|
||||
version: 2.7.53
|
||||
@@ -120,8 +120,8 @@ importers:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
next:
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: 15.1.6
|
||||
version: 15.1.6(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
posthog-js:
|
||||
specifier: ^1.205.0
|
||||
version: 1.205.0
|
||||
@@ -148,8 +148,8 @@ importers:
|
||||
version: 5.0.1(@types/react@18.3.12)(react@19.0.0)
|
||||
devDependencies:
|
||||
'@tauri-apps/cli':
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
specifier: 2.2.7
|
||||
version: 2.2.7
|
||||
'@types/cors':
|
||||
specifier: ^2.8.17
|
||||
version: 2.8.17
|
||||
@@ -1127,60 +1127,60 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.9':
|
||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||
|
||||
'@next/env@15.1.0':
|
||||
resolution: {integrity: sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==}
|
||||
'@next/env@15.1.6':
|
||||
resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==}
|
||||
|
||||
'@next/eslint-plugin-next@15.0.3':
|
||||
resolution: {integrity: sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==}
|
||||
|
||||
'@next/swc-darwin-arm64@15.1.0':
|
||||
resolution: {integrity: sha512-ZU8d7xxpX14uIaFC3nsr4L++5ZS/AkWDm1PzPO6gD9xWhFkOj2hzSbSIxoncsnlJXB1CbLOfGVN4Zk9tg83PUw==}
|
||||
'@next/swc-darwin-arm64@15.1.6':
|
||||
resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-darwin-x64@15.1.0':
|
||||
resolution: {integrity: sha512-DQ3RiUoW2XC9FcSM4ffpfndq1EsLV0fj0/UY33i7eklW5akPUCo6OX2qkcLXZ3jyPdo4sf2flwAED3AAq3Om2Q==}
|
||||
'@next/swc-darwin-x64@15.1.6':
|
||||
resolution: {integrity: sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.0':
|
||||
resolution: {integrity: sha512-M+vhTovRS2F//LMx9KtxbkWk627l5Q7AqXWWWrfIzNIaUFiz2/NkOFkxCFyNyGACi5YbA8aekzCLtbDyfF/v5Q==}
|
||||
'@next/swc-linux-arm64-gnu@15.1.6':
|
||||
resolution: {integrity: sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.0':
|
||||
resolution: {integrity: sha512-Qn6vOuwaTCx3pNwygpSGtdIu0TfS1KiaYLYXLH5zq1scoTXdwYfdZtwvJTpB1WrLgiQE2Ne2kt8MZok3HlFqmg==}
|
||||
'@next/swc-linux-arm64-musl@15.1.6':
|
||||
resolution: {integrity: sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.0':
|
||||
resolution: {integrity: sha512-yeNh9ofMqzOZ5yTOk+2rwncBzucc6a1lyqtg8xZv0rH5znyjxHOWsoUtSq4cUTeeBIiXXX51QOOe+VoCjdXJRw==}
|
||||
'@next/swc-linux-x64-gnu@15.1.6':
|
||||
resolution: {integrity: sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.0':
|
||||
resolution: {integrity: sha512-t9IfNkHQs/uKgPoyEtU912MG6a1j7Had37cSUyLTKx9MnUpjj+ZDKw9OyqTI9OwIIv0wmkr1pkZy+3T5pxhJPg==}
|
||||
'@next/swc-linux-x64-musl@15.1.6':
|
||||
resolution: {integrity: sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@15.1.0':
|
||||
resolution: {integrity: sha512-WEAoHyG14t5sTavZa1c6BnOIEukll9iqFRTavqRVPfYmfegOAd5MaZfXgOGG6kGo1RduyGdTHD4+YZQSdsNZXg==}
|
||||
'@next/swc-win32-arm64-msvc@15.1.6':
|
||||
resolution: {integrity: sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@next/swc-win32-x64-msvc@15.1.0':
|
||||
resolution: {integrity: sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==}
|
||||
'@next/swc-win32-x64-msvc@15.1.6':
|
||||
resolution: {integrity: sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -1623,75 +1623,75 @@ packages:
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
'@tauri-apps/api@2.1.1':
|
||||
resolution: {integrity: sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==}
|
||||
'@tauri-apps/api@2.2.0':
|
||||
resolution: {integrity: sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg==}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.1.0':
|
||||
resolution: {integrity: sha512-ESc6J6CE8hl1yKH2vJ+ALF+thq4Be+DM1mvmTyUCQObvezNCNhzfS6abIUd3ou4x5RGH51ouiANeT3wekU6dCw==}
|
||||
'@tauri-apps/cli-darwin-arm64@2.2.7':
|
||||
resolution: {integrity: sha512-54kcpxZ3X1Rq+pPTzk3iIcjEVY4yv493uRx/80rLoAA95vAC0c//31Whz75UVddDjJfZvXlXZ3uSZ+bnCOnt0A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@tauri-apps/cli-darwin-x64@2.1.0':
|
||||
resolution: {integrity: sha512-TasHS442DFs8cSH2eUQzuDBXUST4ECjCd0yyP+zZzvAruiB0Bg+c8A+I/EnqCvBQ2G2yvWLYG8q/LI7c87A5UA==}
|
||||
'@tauri-apps/cli-darwin-x64@2.2.7':
|
||||
resolution: {integrity: sha512-Vgu2XtBWemLnarB+6LqQeLanDlRj7CeFN//H8bVVdjbNzxcSxsvbLYMBP8+3boa7eBnjDrqMImRySSgL6IrwTw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.1.0':
|
||||
resolution: {integrity: sha512-aP7ZBGNL4ny07Cbb6kKpUOSrmhcIK2KhjviTzYlh+pPhAptxnC78xQGD3zKQkTi2WliJLPmBYbOHWWQa57lQ9w==}
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.2.7':
|
||||
resolution: {integrity: sha512-+Clha2iQAiK9zoY/KKW0KLHkR0k36O78YLx5Sl98tWkwI3OBZFg5H5WT1plH/4sbZIS2aLFN6dw58/JlY9Bu/g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.1.0':
|
||||
resolution: {integrity: sha512-ZTdgD5gLeMCzndMT2f358EkoYkZ5T+Qy6zPzU+l5vv5M7dHVN9ZmblNAYYXmoOuw7y+BY4X/rZvHV9pcGrcanQ==}
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.2.7':
|
||||
resolution: {integrity: sha512-Z/Lp4SQe6BUEOays9BQAEum2pvZF4w9igyXijP+WbkOejZx4cDvarFJ5qXrqSLmBh7vxrdZcLwoLk9U//+yQrg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.1.0':
|
||||
resolution: {integrity: sha512-NzwqjUCilhnhJzusz3d/0i0F1GFrwCQbkwR6yAHUxItESbsGYkZRJk0yMEWkg3PzFnyK4cWTlQJMEU52TjhEzA==}
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.2.7':
|
||||
resolution: {integrity: sha512-+8HZ+txff/Y3YjAh80XcLXcX8kpGXVdr1P8AfjLHxHdS6QD4Md+acSxGTTNbplmHuBaSHJvuTvZf9tU1eDCTDg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.1.0':
|
||||
resolution: {integrity: sha512-TyiIpMEtZxNOQmuFyfJwaaYbg3movSthpBJLIdPlKxSAB2BW0VWLY3/ZfIxm/G2YGHyREkjJvimzYE0i37PnMA==}
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.2.7':
|
||||
resolution: {integrity: sha512-ahlSnuCnUntblp9dG7/w5ZWZOdzRFi3zl0oScgt7GF4KNAOEa7duADsxPA4/FT2hLRa0SvpqtD4IYFvCxoVv3Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-musl@2.1.0':
|
||||
resolution: {integrity: sha512-/dQd0TlaxBdJACrR72DhynWftzHDaX32eBtS5WBrNJ+nnNb+znM3gON6nJ9tSE9jgDa6n1v2BkI/oIDtypfUXw==}
|
||||
'@tauri-apps/cli-linux-x64-musl@2.2.7':
|
||||
resolution: {integrity: sha512-+qKAWnJRSX+pjjRbKAQgTdFY8ecdcu8UdJ69i7wn3ZcRn2nMMzOO2LOMOTQV42B7/Q64D1pIpmZj9yblTMvadA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.1.0':
|
||||
resolution: {integrity: sha512-NdQJO7SmdYqOcE+JPU7bwg7+odfZMWO6g8xF9SXYCMdUzvM2Gv/AQfikNXz5yS7ralRhNFuW32i5dcHlxh4pDg==}
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.2.7':
|
||||
resolution: {integrity: sha512-aa86nRnrwT04u9D9fhf5JVssuAZlUCCc8AjqQjqODQjMd4BMA2+d4K9qBMpEG/1kVh95vZaNsLogjEaqSTTw4A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.1.0':
|
||||
resolution: {integrity: sha512-f5h8gKT/cB8s1ticFRUpNmHqkmaLutT62oFDB7N//2YTXnxst7EpMIn1w+QimxTvTk2gcx6EcW6bEk/y2hZGzg==}
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.2.7':
|
||||
resolution: {integrity: sha512-EiJ5/25tLSQOSGvv+t6o3ZBfOTKB5S3vb+hHQuKbfmKdRF0XQu2YPdIi1CQw1DU97ZAE0Dq4frvnyYEKWgMzVQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.1.0':
|
||||
resolution: {integrity: sha512-P/+LrdSSb5Xbho1LRP4haBjFHdyPdjWvGgeopL96OVtrFpYnfC+RctB45z2V2XxqFk3HweDDxk266btjttfjGw==}
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.2.7':
|
||||
resolution: {integrity: sha512-ZB8Kw90j8Ld+9tCWyD2fWCYfIrzbQohJ4DJSidNwbnehlZzP7wAz6Z3xjsvUdKtQ3ibtfoeTqVInzCCEpI+pWg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli@2.1.0':
|
||||
resolution: {integrity: sha512-K2VhcKqBhAeS5pNOVdnR/xQRU6jwpgmkSL2ejHXcl0m+kaTggT0WRDQnFtPq6NljA7aE03cvwsbCAoFG7vtkJw==}
|
||||
'@tauri-apps/cli@2.2.7':
|
||||
resolution: {integrity: sha512-ZnsS2B4BplwXP37celanNANiIy8TCYhvg5RT09n72uR/o+navFZtGpFSqljV8fy1Y4ixIPds8FrGSXJCN2BerA==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1707,14 +1707,14 @@ packages:
|
||||
'@tauri-apps/plugin-fs@2.2.0':
|
||||
resolution: {integrity: sha512-+08mApuONKI8/sCNEZ6AR8vf5vI9DXD4YfrQ9NQmhRxYKMLVhRW164vdW5BSLmMpuevftpQ2FVoL9EFkfG9Z+g==}
|
||||
|
||||
'@tauri-apps/plugin-http@2.2.0':
|
||||
resolution: {integrity: sha512-ZY6sIHhgu8hcu6BkkegoiOEbvOsQFSVcK8J7l+g9RNHrkhl5uzpNIytR4R/H50fj7gyG80DJvrXDx/LBo7Easw==}
|
||||
'@tauri-apps/plugin-http@2.3.0':
|
||||
resolution: {integrity: sha512-pigTvz+zzAqbIhCzRiR1GE98Jw7A03j2V+Eiexr9thBI8VfMiwFQMcbgON51xlwnVaI72LdbYKNajU84im8tlg==}
|
||||
|
||||
'@tauri-apps/plugin-log@2.2.0':
|
||||
resolution: {integrity: sha512-g6CsQAR1lsm5ABSZZxpM/iCn86GrMDTTlhj7GPkZkYBRSm3+WczfOAl7SV7HDn77tOKCzhZffwI5uHfRoHutrw==}
|
||||
'@tauri-apps/plugin-log@2.2.1':
|
||||
resolution: {integrity: sha512-bOz9w0hhlXLGLc1ZR37GqkXvTqkykl4A3GEKLjRIs0dq3n0BzLyoRDMPcpt7PdUHqaq6WISME+zEX2bqjSbJ2A==}
|
||||
|
||||
'@tauri-apps/plugin-opener@2.2.2':
|
||||
resolution: {integrity: sha512-E/XIHKqGV+FT8PDdkfMETmgPUxcR79Rk8USuzbadD/ZdvsKCfQR5q+6rpZC9zEnG2wzi9lVQM4D3xwrtGGIB8A==}
|
||||
'@tauri-apps/plugin-opener@2.2.5':
|
||||
resolution: {integrity: sha512-hHsJ9RPWpZvZEPVFaL+d25gABMUMOf/A6ESXnvf/ii9guTukj58WXsAE/SOysXRIhej7kseRCxnOnIMpSCdUsQ==}
|
||||
|
||||
'@tauri-apps/plugin-os@2.2.0':
|
||||
resolution: {integrity: sha512-HszbCdbisMlu5QhCNAN8YIWyz2v33abAWha6+uvV2CKX8P5VSct/y+kEe22JeyqrxCnWlQ3DRx7s49Byg7/0EA==}
|
||||
@@ -1725,8 +1725,8 @@ packages:
|
||||
'@tauri-apps/plugin-shell@2.2.0':
|
||||
resolution: {integrity: sha512-iC3Ic1hLmasoboG7BO+7p+AriSoqAwKrIk+Hpk+S/bjTQdXqbl2GbdclghI4gM32X0bls7xHzIFqhRdrlvJeaA==}
|
||||
|
||||
'@tauri-apps/plugin-updater@2.3.0':
|
||||
resolution: {integrity: sha512-qdzyZEUN69FZQ/nRx51fBub10tT6wffJl3DLVo9q922Gvw8Wk++rZhoD9eethPlZYbog/7RGgT8JkrfLh5BKAg==}
|
||||
'@tauri-apps/plugin-updater@2.4.0':
|
||||
resolution: {integrity: sha512-BkeKN2WObAjobf2G77HyW/DxAfI0In+VSqWGnw/0cVPlM+VmA7fw9dKUnSunryZOG7ys9y07tj7FQa1ABMXGZQ==}
|
||||
|
||||
'@tsconfig/node10@1.0.11':
|
||||
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
|
||||
@@ -3356,8 +3356,8 @@ packages:
|
||||
neo-async@2.6.2:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
|
||||
next@15.1.0:
|
||||
resolution: {integrity: sha512-QKhzt6Y8rgLNlj30izdMbxAwjHMFANnLwDwZ+WQh5sMhyt4lEBqDK9QpvWHtIM4rINKPoJ8aiRZKg5ULSybVHw==}
|
||||
next@15.1.6:
|
||||
resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==}
|
||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -5705,10 +5705,10 @@ snapshots:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
optional: true
|
||||
|
||||
'@ducanh2912/next-pwa@10.2.9(next@15.1.0(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(webpack@5.97.1)':
|
||||
'@ducanh2912/next-pwa@10.2.9(next@15.1.6(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(webpack@5.97.1)':
|
||||
dependencies:
|
||||
fast-glob: 3.3.2
|
||||
next: 15.1.0(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next: 15.1.6(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
semver: 7.6.3
|
||||
webpack: 5.97.1
|
||||
workbox-build: 7.1.1
|
||||
@@ -5765,7 +5765,7 @@ snapshots:
|
||||
|
||||
'@fabianlars/tauri-plugin-oauth@2.0.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@gulpjs/to-absolute-glob@4.0.0':
|
||||
dependencies:
|
||||
@@ -5896,34 +5896,34 @@ snapshots:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
optional: true
|
||||
|
||||
'@next/env@15.1.0': {}
|
||||
'@next/env@15.1.6': {}
|
||||
|
||||
'@next/eslint-plugin-next@15.0.3':
|
||||
dependencies:
|
||||
fast-glob: 3.3.1
|
||||
|
||||
'@next/swc-darwin-arm64@15.1.0':
|
||||
'@next/swc-darwin-arm64@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-darwin-x64@15.1.0':
|
||||
'@next/swc-darwin-x64@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.0':
|
||||
'@next/swc-linux-arm64-gnu@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.0':
|
||||
'@next/swc-linux-arm64-musl@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.0':
|
||||
'@next/swc-linux-x64-gnu@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.0':
|
||||
'@next/swc-linux-x64-musl@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-arm64-msvc@15.1.0':
|
||||
'@next/swc-win32-arm64-msvc@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-x64-msvc@15.1.0':
|
||||
'@next/swc-win32-x64-msvc@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
@@ -6480,94 +6480,94 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tauri-apps/api@2.1.1': {}
|
||||
'@tauri-apps/api@2.2.0': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.1.0':
|
||||
'@tauri-apps/cli-darwin-arm64@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-darwin-x64@2.1.0':
|
||||
'@tauri-apps/cli-darwin-x64@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.1.0':
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.1.0':
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.1.0':
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.1.0':
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-x64-musl@2.1.0':
|
||||
'@tauri-apps/cli-linux-x64-musl@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.1.0':
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.1.0':
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.1.0':
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.2.7':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli@2.1.0':
|
||||
'@tauri-apps/cli@2.2.7':
|
||||
optionalDependencies:
|
||||
'@tauri-apps/cli-darwin-arm64': 2.1.0
|
||||
'@tauri-apps/cli-darwin-x64': 2.1.0
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf': 2.1.0
|
||||
'@tauri-apps/cli-linux-arm64-gnu': 2.1.0
|
||||
'@tauri-apps/cli-linux-arm64-musl': 2.1.0
|
||||
'@tauri-apps/cli-linux-x64-gnu': 2.1.0
|
||||
'@tauri-apps/cli-linux-x64-musl': 2.1.0
|
||||
'@tauri-apps/cli-win32-arm64-msvc': 2.1.0
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.1.0
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.1.0
|
||||
'@tauri-apps/cli-darwin-arm64': 2.2.7
|
||||
'@tauri-apps/cli-darwin-x64': 2.2.7
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf': 2.2.7
|
||||
'@tauri-apps/cli-linux-arm64-gnu': 2.2.7
|
||||
'@tauri-apps/cli-linux-arm64-musl': 2.2.7
|
||||
'@tauri-apps/cli-linux-x64-gnu': 2.2.7
|
||||
'@tauri-apps/cli-linux-x64-musl': 2.2.7
|
||||
'@tauri-apps/cli-win32-arm64-msvc': 2.2.7
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.2.7
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.2.7
|
||||
|
||||
'@tauri-apps/plugin-cli@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-deep-link@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-fs@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-http@2.2.0':
|
||||
'@tauri-apps/plugin-http@2.3.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-log@2.2.0':
|
||||
'@tauri-apps/plugin-log@2.2.1':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-opener@2.2.2':
|
||||
'@tauri-apps/plugin-opener@2.2.5':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-os@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-process@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-shell@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-updater@2.3.0':
|
||||
'@tauri-apps/plugin-updater@2.4.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tsconfig/node10@1.0.11':
|
||||
optional: true
|
||||
@@ -8378,26 +8378,26 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
next@15.1.0(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
next@15.1.6(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@next/env': 15.1.0
|
||||
'@next/env': 15.1.6
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/helpers': 0.5.15
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: 1.0.30001684
|
||||
caniuse-lite: 1.0.30001695
|
||||
postcss: 8.4.31
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
styled-jsx: 5.1.6(@babel/core@7.26.7)(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 15.1.0
|
||||
'@next/swc-darwin-x64': 15.1.0
|
||||
'@next/swc-linux-arm64-gnu': 15.1.0
|
||||
'@next/swc-linux-arm64-musl': 15.1.0
|
||||
'@next/swc-linux-x64-gnu': 15.1.0
|
||||
'@next/swc-linux-x64-musl': 15.1.0
|
||||
'@next/swc-win32-arm64-msvc': 15.1.0
|
||||
'@next/swc-win32-x64-msvc': 15.1.0
|
||||
'@next/swc-darwin-arm64': 15.1.6
|
||||
'@next/swc-darwin-x64': 15.1.6
|
||||
'@next/swc-linux-arm64-gnu': 15.1.6
|
||||
'@next/swc-linux-arm64-musl': 15.1.6
|
||||
'@next/swc-linux-x64-gnu': 15.1.6
|
||||
'@next/swc-linux-x64-musl': 15.1.6
|
||||
'@next/swc-win32-arm64-msvc': 15.1.6
|
||||
'@next/swc-win32-x64-msvc': 15.1.6
|
||||
sharp: 0.33.5
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
|
||||