Compare commits

..

1 Commits

Author SHA1 Message Date
chrox 90fee08be2 Release version 0.8.8 2024-12-29 12:48:55 +01:00
83 changed files with 256 additions and 1208 deletions
-13
View File
@@ -1,13 +0,0 @@
# Keep GitHub Actions up to date with GitHub's Dependabot...
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
version: 2
updates:
- package-ecosystem: github-actions
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly
-35
View File
@@ -1,35 +0,0 @@
name: Build Web Application on Pull Request
on:
pull_request:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
build_web_app:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.1
- name: setup node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: install Dependencies
working-directory: apps/readest-app
run: |
pnpm install && pnpm setup-pdfjs
- name: build the web App
working-directory: apps/readest-app
run: |
pnpm build-web
+12 -40
View File
@@ -10,20 +10,16 @@ jobs:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
release_id: ${{ steps.get-release.outputs.release_id }}
release_tag: ${{ steps.get-release.outputs.release_tag }}
release_note: ${{ steps.get-release-notes.outputs.release_note }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v4
uses: actions/setup-node@v3
- name: get version
run: echo "PACKAGE_VERSION=$(node -p "require('./apps/readest-app/package.json').version")" >> $GITHUB_ENV
- name: get release
id: get-release
uses: actions/github-script@v7
uses: actions/github-script@v6
with:
script: |
const { data } = await github.rest.repos.getLatestRelease({
@@ -31,10 +27,9 @@ jobs:
repo: context.repo.repo,
})
core.setOutput('release_id', data.id);
core.setOutput('release_tag', data.tag_name);
- name: get release notes
id: get-release-notes
uses: actions/github-script@v7
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
@@ -73,7 +68,7 @@ jobs:
runs-on: ${{ matrix.config.os }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: initialize git submodules
run: git submodule update --init --recursive
@@ -84,7 +79,7 @@ jobs:
version: 9.14.4
- name: setup node
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 22
cache: pnpm
@@ -110,8 +105,6 @@ jobs:
echo "NEXT_PUBLIC_POSTHOG_KEY=${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}" >> .env.local
echo "NEXT_PUBLIC_POSTHOG_HOST=${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}" >> .env.local
echo "NEXT_PUBLIC_DEEPL_API_KEY=${{ secrets.NEXT_PUBLIC_DEEPL_API_KEY }}" >> .env.local
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env.local
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}" >> .env.local
echo "NEXT_PUBLIC_APP_PLATFORM=tauri" >> .env.local
- name: copy .env.local to apps/readest-app
@@ -136,30 +129,10 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
projectPath: apps/readest-app
releaseId: ${{ needs.get-release.outputs.release_id }}
releaseBody: ${{ needs.get-release.outputs.release_note }}
releaseId: ${{ needs.get-release.steps.get-release.outputs.release_id }}
releaseBody: ${{ needs.get-release.steps.get-release-notes.outputs.release_note }}
args: ${{ matrix.config.args || '' }}
- name: upload portable binaries (Windows only)
if: matrix.config.os == 'windows-latest'
run: |
echo "Uploading Portable Binaries"
arch=${{ matrix.config.arch }}
version=$PACKAGE_VERSION
if [ "$arch" = "x86_64" ]; then
zip_name="Readest_${version}_x64-portable.zip"
elif [ "$arch" = "aarch64" ]; then
zip_name="Readest_${version}_arm64-portable.zip"
else
echo "Unknown architecture: $arch"
exit 1
fi
zip -j $zip_name apps/readest-app/src-tauri/target/release/readest.exe
echo "Uploading $zip_name to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} $zip_name --clobber
update-release:
permissions:
contents: write
@@ -169,18 +142,17 @@ jobs:
steps:
- name: update release
id: update-release
uses: actions/github-script@v7
uses: actions/github-script@v6
env:
release_id: ${{ needs.get-release.outputs.release_id }}
release_note: ${{ needs.get-release.outputs.release_note }}
release_id: ${{ needs.get-release.steps.get-release.outputs.release_id }}
release_note: ${{ needs.get-release.steps.get-release-notes.outputs.release_note }}
with:
script: |
const body = `## Release Highlight\n${process.env.release_note}`;
github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.release_id,
body: body,
body: process.env.release_note,
draft: false,
prerelease: false
})
+25
View File
@@ -0,0 +1,25 @@
name: Create vercel preview URL on pull request
on:
pull_request:
branches: [main, master]
permissions:
contents: write
deployments: write
pull-requests: write
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- uses: amondnet/vercel-action@v25
id: vercel-deploy
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID}}
vercel-project-id: ${{ secrets.PROJECT_ID}}
- name: preview-url
run: |
echo ${{ steps.vercel-deploy.outputs.preview-url }}
+11 -22
View File
@@ -1,6 +1,6 @@
<div align="center">
<a href="https://readest.com?utm_source=github&utm_medium=referral&utm_campaign=readme" target="_blank">
<img src="https://github.com/readest/readest/blob/main/apps/readest-app/src-tauri/icons/icon.png?raw=true" alt="Readest Logo" width="20%" />
<img src="https://github.com/chrox/readest/blob/main/apps/readest-app/src-tauri/icons/icon.png?raw=true" alt="Readest Logo" width="20%" />
</a>
<h1>Readest</h1>
<br>
@@ -118,7 +118,7 @@ To get started with Readest, follow these steps to clone and build the project.
### 1. Clone the Repository
```bash
git clone https://github.com/readest/readest.git
git clone https://github.com/chrox/readest.git
cd readest
git submodule update --init --recursive
```
@@ -150,29 +150,19 @@ For Windows targets, “Build Tools for Visual Studio 2022” (or a higher editi
pnpm tauri dev
```
For iOS:
```bash
pnpm tauri ios dev
```
### 5. Build for Production
```bash
pnpm tauri build
```
### 6. More information
Please check the [wiki][link-gh-wiki] of this project for more information on development.
## Contributors
Readest is open-source, and contributions are welcome! Feel free to open issues, suggest features, or submit pull requests. Please **review our [contributing guidelines](CONTRIBUTING.md) before you start**. We also welcome you to join our [Discord][link-discord] community for either support or contributing guidance.
<a href="https://github.com/readest/readest/graphs/contributors">
<a href="https://github.com/chrox/readest/graphs/contributors">
<p align="left">
<img width="200" src="https://contrib.rocks/image?repo=readest/readest" alt="A table of avatars from the project's contributors" />
<img width="100" src="https://contrib.rocks/image?repo=chrox/readest" alt="A table of avatars from the project's contributors" />
</p>
</a>
@@ -193,19 +183,18 @@ The following JavaScript libraries are bundled in this software:
[badge-website]: https://img.shields.io/badge/website-readest.com-orange
[badge-web-app]: https://img.shields.io/badge/read%20online-web.readest.com-orange
[badge-license]: https://img.shields.io/github/license/readest/readest?color=teal
[badge-release]: https://img.shields.io/github/release/readest/readest?color=green
[badge-license]: https://img.shields.io/github/license/chrox/readest?color=teal
[badge-release]: https://img.shields.io/github/release/chrox/readest?color=green
[badge-platforms]: https://img.shields.io/badge/OS-macOS%2C%20Windows%2C%20Linux%2C%20Web-green
[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-last-commit]: https://img.shields.io/github/last-commit/chrox/readest?color=green
[badge-commit-activity]: https://img.shields.io/github/commit-activity/m/chrox/readest
[badge-discord]: https://img.shields.io/discord/1314226120886976544?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square
[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
[link-gh-releases]: https://github.com/readest/readest/releases
[link-gh-commits]: https://github.com/readest/readest/commits/main
[link-gh-pulse]: https://github.com/readest/readest/pulse
[link-gh-wiki]: https://github.com/readest/readest/wiki
[link-gh-releases]: https://github.com/chrox/readest/releases
[link-gh-commits]: https://github.com/chrox/readest/commits/main
[link-gh-pulse]: https://github.com/chrox/readest/pulse
[link-discord]: https://discord.gg/gntyVNk3BJ
[link-parallel-read]: https://readest.com/#parallel-read
[link-koreader]: https://github.com/koreader/koreader
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@readest/readest-app",
"version": "0.9.0",
"version": "0.8.8",
"private": true,
"scripts": {
"dev": "dotenv -e .env.tauri -- next dev",
@@ -35,7 +35,6 @@
"@supabase/supabase-js": "^2.47.7",
"@tauri-apps/api": "2.1.1",
"@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",
@@ -2,7 +2,6 @@
"About Readest": "Über Readest",
"Add your notes here...": "Fügen Sie hier Ihre Notizen hinzu...",
"Animation": "Animation",
"Apply": "Anwenden",
"Are you sure to delete the selected books?": "Möchten Sie die ausgewählten Bücher wirklich löschen?",
"Auto Mode": "Automatischer Modus",
"Behavior": "Verhalten",
@@ -26,7 +25,6 @@
"Edit": "Bearbeiten",
"Enter your custom CSS here...": "Geben Sie hier Ihr benutzerdefiniertes CSS ein...",
"Excerpts": "Auszüge",
"Failed to import book(s): {{filenames}}": "Fehler beim Importieren von Buch/Büchern: {{filenames}}",
"Font": "Schriftart",
"Font & Layout": "Schrift & Layout",
"Font Face": "Schriftschnitt",
@@ -43,7 +41,6 @@
"Grass": "Grasgrün",
"Gray": "Grau",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Horizontale Richtung",
"Hyphenation": "Silbentrennung",
"Identifier:": "Kennung:",
"Import Books": "Bücher importieren",
@@ -71,7 +68,6 @@
"Notebook": "Notizbuch",
"Notes": "Notizen",
"Open": "Öffnen",
"Open Book": "Buch öffnen",
"Override Publisher Font": "Verleger-Schriftart überschreiben",
"Page": "Seite",
"Paging Animation": "Blätter-Animation",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Scroll-Modus",
"Search books...": "Bücher suchen...",
"Search...": "Suchen...",
"Select Book": "Buch auswählen",
"Select books": "Bücher auswählen",
"Select multiple books": "Mehrere Bücher auswählen",
"Sepia": "Sepia",
"Serif Font": "Serifenschrift",
"Show Book Details": "Buchdetails anzeigen",
"Sidebar": "Seitenleiste",
"Sign In": "Anmelden",
"Sign Out": "Abmelden",
"Sky": "Himmelblau",
"Solarized": "Solarisiert",
"Subjects:": "Themen:",
"System Fonts": "System-Schriftarten",
"Theme Color": "Themenfarbe",
"Theme Mode": "Themenmodus",
"Unknown": "Unbekannt",
@@ -109,8 +102,6 @@
"Updated:": "Aktualisiert:",
"User avatar": "Benutzer-Avatar",
"Version {{version}}": "Version {{version}}",
"Vertical Direction": "Vertikale Richtung",
"Welcome to your library. You can import your books here and read them anytime.": "Willkommen in Ihrer Bibliothek. Sie können hier Ihre Bücher importieren und jederzeit lesen.",
"Writing Mode": "Schreibmodus",
"Your Library": "Ihre Bibliothek"
}
@@ -2,7 +2,6 @@
"About Readest": "Acerca de Readest",
"Add your notes here...": "Añade tus notas aquí...",
"Animation": "Animación",
"Apply": "Aplicar",
"Are you sure to delete the selected books?": "¿Estás seguro de eliminar los libros seleccionados?",
"Auto Mode": "Modo automático",
"Behavior": "Comportamiento",
@@ -26,7 +25,6 @@
"Edit": "Editar",
"Enter your custom CSS here...": "Introduce tu CSS personalizado aquí...",
"Excerpts": "Extractos",
"Failed to import book(s): {{filenames}}": "Error al importar libro(s): {{filenames}}",
"Font": "Fuente",
"Font & Layout": "Fuente y diseño",
"Font Face": "Tipo de fuente",
@@ -43,7 +41,6 @@
"Grass": "Hierba",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Dirección horizontal",
"Hyphenation": "Guionización",
"Identifier:": "Identificador:",
"Import Books": "Importar libros",
@@ -71,7 +68,6 @@
"Notebook": "Cuaderno",
"Notes": "Notas",
"Open": "Abrir",
"Open Book": "Abrir libro",
"Override Publisher Font": "Sobrescribir fuente del editor",
"Page": "Página",
"Paging Animation": "Animación de paginación",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Modo desplazamiento",
"Search books...": "Buscar libros...",
"Search...": "Buscar...",
"Select Book": "Seleccionar libro",
"Select books": "Seleccionar libros",
"Select multiple books": "Seleccionar varios libros",
"Sepia": "Sepia",
"Serif Font": "Fuente serif",
"Show Book Details": "Mostrar detalles del libro",
"Sidebar": "Barra lateral",
"Sign In": "Iniciar sesión",
"Sign Out": "Cerrar sesión",
"Sky": "Cielo",
"Solarized": "Solarizado",
"Subjects:": "Temas:",
"System Fonts": "Fuentes del sistema",
"Theme Color": "Color del tema",
"Theme Mode": "Modo del tema",
"Unknown": "Desconocido",
@@ -109,8 +102,6 @@
"Updated:": "Actualizado:",
"User avatar": "Avatar del usuario",
"Version {{version}}": "Versión {{version}}",
"Vertical Direction": "Dirección vertical",
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenido a tu biblioteca. Puedes importar tus libros aquí y leerlos en cualquier momento.",
"Writing Mode": "Modo de escritura",
"Your Library": "Tu biblioteca"
}
@@ -2,7 +2,6 @@
"About Readest": "À propos de Readest",
"Add your notes here...": "Ajoutez vos notes ici...",
"Animation": "Animation",
"Apply": "Appliquer",
"Are you sure to delete the selected books?": "Êtes-vous sûr de vouloir supprimer les livres sélectionnés ?",
"Auto Mode": "Mode automatique",
"Behavior": "Comportement",
@@ -26,7 +25,6 @@
"Edit": "Modifier",
"Enter your custom CSS here...": "Saisissez votre CSS personnalisé ici...",
"Excerpts": "Extraits",
"Failed to import book(s): {{filenames}}": "Échec de l'importation du/des livre(s) : {{filenames}}",
"Font": "Police",
"Font & Layout": "Police et mise en page",
"Font Face": "Style de police",
@@ -43,7 +41,6 @@
"Grass": "Herbe",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Direction horizontale",
"Hyphenation": "Césure",
"Identifier:": "Identifiant :",
"Import Books": "Importer des livres",
@@ -71,7 +68,6 @@
"Notebook": "Carnet de notes",
"Notes": "Notes",
"Open": "Ouvrir",
"Open Book": "Ouvrir le livre",
"Override Publisher Font": "Remplacer la police de l'éditeur",
"Page": "Page",
"Paging Animation": "Animation de page",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Mode défilement",
"Search books...": "Rechercher des livres...",
"Search...": "Rechercher...",
"Select Book": "Sélectionner un livre",
"Select books": "Sélectionner des livres",
"Select multiple books": "Sélectionner plusieurs livres",
"Sepia": "Sépia",
"Serif Font": "Police avec empattement",
"Show Book Details": "Afficher les détails du livre",
"Sidebar": "Barre latérale",
"Sign In": "Se connecter",
"Sign Out": "Se déconnecter",
"Sky": "Ciel",
"Solarized": "Solarisé",
"Subjects:": "Sujets :",
"System Fonts": "Polices système",
"Theme Color": "Couleur du thème",
"Theme Mode": "Mode du thème",
"Unknown": "Inconnu",
@@ -109,8 +102,6 @@
"Updated:": "Mis à jour :",
"User avatar": "Avatar de l'utilisateur",
"Version {{version}}": "Version {{version}}",
"Vertical Direction": "Direction verticale",
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenue dans votre bibliothèque. Vous pouvez importer vos livres ici et les lire à tout moment.",
"Writing Mode": "Mode écriture",
"Your Library": "Votre bibliothèque"
}
@@ -2,7 +2,6 @@
"About Readest": "Tentang Readest",
"Add your notes here...": "Tambahkan catatan Anda di sini...",
"Animation": "Animasi",
"Apply": "Terapkan",
"Are you sure to delete the selected books?": "Apakah Anda yakin ingin menghapus buku yang dipilih?",
"Auto Mode": "Mode Otomatis",
"Behavior": "Perilaku",
@@ -26,7 +25,6 @@
"Edit": "Edit",
"Enter your custom CSS here...": "Masukkan CSS kustom Anda di sini...",
"Excerpts": "Kutipan",
"Failed to import book(s): {{filenames}}": "Gagal mengimpor buku: {{filenames}}",
"Font": "Font",
"Font & Layout": "Font & Tata Letak",
"Font Face": "Jenis Font",
@@ -43,7 +41,6 @@
"Grass": "Rumput",
"Gray": "Abu-abu",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Arah Horizontal",
"Hyphenation": "Pemenggalan Kata",
"Identifier:": "Pengenal:",
"Import Books": "Impor Buku",
@@ -71,7 +68,6 @@
"Notebook": "Buku Catatan",
"Notes": "Catatan",
"Open": "Buka",
"Open Book": "Buka Buku",
"Override Publisher Font": "Ganti Font Penerbit",
"Page": "Halaman",
"Paging Animation": "Animasi Halaman",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Mode Gulir",
"Search books...": "Cari buku...",
"Search...": "Cari...",
"Select Book": "Pilih Buku",
"Select books": "Pilih buku",
"Select multiple books": "Pilih beberapa buku",
"Sepia": "Sepia",
"Serif Font": "Font Serif",
"Show Book Details": "Tampilkan Detail Buku",
"Sidebar": "Bilah Samping",
"Sign In": "Masuk",
"Sign Out": "Keluar",
"Sky": "Langit",
"Solarized": "Solarized",
"Subjects:": "Subjek:",
"System Fonts": "Font Sistem",
"Theme Color": "Warna Tema",
"Theme Mode": "Mode Tema",
"Unknown": "Tidak Diketahui",
@@ -109,8 +102,6 @@
"Updated:": "Diperbarui:",
"User avatar": "Avatar Pengguna",
"Version {{version}}": "Versi {{version}}",
"Vertical Direction": "Arah Vertikal",
"Welcome to your library. You can import your books here and read them anytime.": "Selamat datang di perpustakaan Anda. Anda dapat mengimpor buku-buku Anda di sini dan membacanya kapan saja.",
"Writing Mode": "Mode Menulis",
"Your Library": "Perpustakaan Anda"
}
@@ -2,7 +2,6 @@
"About Readest": "Informazioni su Readest",
"Add your notes here...": "Aggiungi qui le tue note...",
"Animation": "Animazione",
"Apply": "Applica",
"Are you sure to delete the selected books?": "Sei sicuro di voler eliminare i libri selezionati?",
"Auto Mode": "Modalità automatica",
"Behavior": "Comportamento",
@@ -26,7 +25,6 @@
"Edit": "Modifica",
"Enter your custom CSS here...": "Inserisci qui il tuo CSS personalizzato...",
"Excerpts": "Estratti",
"Failed to import book(s): {{filenames}}": "Impossibile importare il/i libro/i: {{filenames}}",
"Font": "Font",
"Font & Layout": "Font e Layout",
"Font Face": "Tipo di carattere",
@@ -43,7 +41,6 @@
"Grass": "Erba",
"Gray": "Grigio",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Direzione orizzontale",
"Hyphenation": "Sillabazione",
"Identifier:": "Identificatore:",
"Import Books": "Importa libri",
@@ -71,7 +68,6 @@
"Notebook": "Quaderno",
"Notes": "Note",
"Open": "Apri",
"Open Book": "Apri libro",
"Override Publisher Font": "Sostituisci font editore",
"Page": "Pagina",
"Paging Animation": "Animazione cambio pagina",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Modalità scorrimento",
"Search books...": "Cerca libri...",
"Search...": "Cerca...",
"Select Book": "Seleziona libro",
"Select books": "Seleziona libri",
"Select multiple books": "Seleziona più libri",
"Sepia": "Seppia",
"Serif Font": "Font serif",
"Show Book Details": "Mostra dettagli libro",
"Sidebar": "Barra laterale",
"Sign In": "Accedi",
"Sign Out": "Esci",
"Sky": "Cielo",
"Solarized": "Solarizzato",
"Subjects:": "Argomenti:",
"System Fonts": "Font di sistema",
"Theme Color": "Colore tema",
"Theme Mode": "Modalità tema",
"Unknown": "Sconosciuto",
@@ -109,8 +102,6 @@
"Updated:": "Aggiornato:",
"User avatar": "Avatar utente",
"Version {{version}}": "Versione {{version}}",
"Vertical Direction": "Direzione verticale",
"Welcome to your library. You can import your books here and read them anytime.": "Benvenuto nella tua biblioteca. Puoi importare i tuoi libri qui e leggerli in qualsiasi momento.",
"Writing Mode": "Modalità scrittura",
"Your Library": "La tua biblioteca"
}
@@ -2,7 +2,6 @@
"About Readest": "Readestについて",
"Add your notes here...": "ここにメモを追加...",
"Animation": "アニメーション",
"Apply": "適用",
"Are you sure to delete the selected books?": "選択した書籍を削除してもよろしいですか?",
"Auto Mode": "自動モード",
"Behavior": "動作",
@@ -26,7 +25,6 @@
"Edit": "編集",
"Enter your custom CSS here...": "ここにカスタムCSSを入力...",
"Excerpts": "抜粋",
"Failed to import book(s): {{filenames}}": "書籍のインポートに失敗しました:{{filenames}}",
"Font": "フォント",
"Font & Layout": "フォントとレイアウト",
"Font Face": "書体",
@@ -43,7 +41,6 @@
"Grass": "グリーン",
"Gray": "グレー",
"Gruvbox": "グルーブボックス",
"Horizontal Direction": "横書",
"Hyphenation": "ハイフネーション",
"Identifier:": "識別子:",
"Import Books": "書籍をインポート",
@@ -71,7 +68,6 @@
"Notebook": "ノート",
"Notes": "メモ",
"Open": "開く",
"Open Book": "書籍を開く",
"Override Publisher Font": "出版社フォントを上書き",
"Page": "ページ",
"Paging Animation": "ページめくりアニメーション",
@@ -89,19 +85,16 @@
"Scrolled Mode": "スクロールモード",
"Search books...": "書籍を検索...",
"Search...": "検索...",
"Select Book": "書籍を選択",
"Select books": "書籍を選択",
"Select multiple books": "複数の書籍を選択",
"Sepia": "セピア",
"Serif Font": "明朝体",
"Show Book Details": "書籍の詳細を表示",
"Sidebar": "サイドバー",
"Sign In": "サインイン",
"Sign Out": "サインアウト",
"Sky": "スカイ",
"Solarized": "ソーラライズド",
"Subjects:": "主題:",
"System Fonts": "システムフォント",
"Theme Color": "テーマカラー",
"Theme Mode": "テーマモード",
"Unknown": "不明",
@@ -109,8 +102,6 @@
"Updated:": "更新日:",
"User avatar": "ユーザーアバター",
"Version {{version}}": "バージョン {{version}}",
"Vertical Direction": "縦書",
"Welcome to your library. You can import your books here and read them anytime.": "ライブラリーへようこそ。ここに書籍をインポートして、いつでも読むことができます。",
"Writing Mode": "書き込みモード",
"Your Library": "ライブラリー"
}
@@ -2,7 +2,6 @@
"About Readest": "Readest 정보",
"Add your notes here...": "여기에 메모를 추가하세요...",
"Animation": "애니메이션",
"Apply": "적용",
"Are you sure to delete the selected books?": "선택한 책을 삭제하시겠습니까?",
"Auto Mode": "자동 모드",
"Behavior": "동작",
@@ -26,7 +25,6 @@
"Edit": "편집",
"Enter your custom CSS here...": "여기에 사용자 정의 CSS를 입력하세요...",
"Excerpts": "발췌",
"Failed to import book(s): {{filenames}}": "책 가져오기 실패: {{filenames}}",
"Font": "글꼴",
"Font & Layout": "글꼴 및 레이아웃",
"Font Face": "글꼴 스타일",
@@ -43,7 +41,6 @@
"Grass": "잔디색",
"Gray": "회색",
"Gruvbox": "그러브박스",
"Horizontal Direction": "수평 방향",
"Hyphenation": "하이픈 넣기",
"Identifier:": "식별자:",
"Import Books": "책 가져오기",
@@ -71,7 +68,6 @@
"Notebook": "노트북",
"Notes": "메모",
"Open": "열기",
"Open Book": "책 열기",
"Override Publisher Font": "출판사 글꼴 재정의",
"Page": "페이지",
"Paging Animation": "페이지 넘김 애니메이션",
@@ -89,19 +85,16 @@
"Scrolled Mode": "스크롤 모드",
"Search books...": "책 검색...",
"Search...": "검색...",
"Select Book": "책 선택",
"Select books": "책 선택",
"Select multiple books": "여러 책 선택",
"Sepia": "세피아",
"Serif Font": "세리프체",
"Show Book Details": "책 세부 정보 표시",
"Sidebar": "사이드바",
"Sign In": "로그인",
"Sign Out": "로그아웃",
"Sky": "하늘색",
"Solarized": "솔라라이즈드",
"Subjects:": "주제:",
"System Fonts": "시스템 글꼴",
"Theme Color": "테마 색상",
"Theme Mode": "테마 모드",
"Unknown": "알 수 없음",
@@ -109,8 +102,6 @@
"Updated:": "업데이트일:",
"User avatar": "사용자 아바타",
"Version {{version}}": "버전 {{version}}",
"Vertical Direction": "수직 방향",
"Welcome to your library. You can import your books here and read them anytime.": "내 서재에 오신 것을 환영합니다. 여기에서 책을 가져와 언제든지 읽을 수 있습니다.",
"Writing Mode": "글쓰기 모드",
"Your Library": "내 서재"
}
@@ -2,7 +2,6 @@
"About Readest": "Sobre o Readest",
"Add your notes here...": "Adicione suas notas aqui...",
"Animation": "Animação",
"Apply": "Aplicar",
"Are you sure to delete the selected books?": "Tem certeza de que deseja excluir os livros selecionados?",
"Auto Mode": "Modo Automático",
"Behavior": "Comportamento",
@@ -26,7 +25,6 @@
"Edit": "Editar",
"Enter your custom CSS here...": "Insira seu CSS personalizado aqui...",
"Excerpts": "Trechos",
"Failed to import book(s): {{filenames}}": "Falha ao importar livro(s): {{filenames}}",
"Font": "Fonte",
"Font & Layout": "Fonte e Layout",
"Font Face": "Estilo da Fonte",
@@ -43,7 +41,6 @@
"Grass": "Grama",
"Gray": "Cinza",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Direção Horizontal",
"Hyphenation": "Hifenização",
"Identifier:": "Identificador:",
"Import Books": "Importar Livros",
@@ -71,7 +68,6 @@
"Notebook": "Caderno",
"Notes": "Notas",
"Open": "Abrir",
"Open Book": "Abrir Livro",
"Override Publisher Font": "Substituir Fonte do Editor",
"Page": "Página",
"Paging Animation": "Animação de Página",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Modo de Rolagem",
"Search books...": "Procurar livros...",
"Search...": "Pesquisar...",
"Select Book": "Selecionar Livro",
"Select books": "Selecionar livros",
"Select multiple books": "Selecionar múltiplos livros",
"Sepia": "Sépia",
"Serif Font": "Fonte Serif",
"Show Book Details": "Mostrar Detalhes do Livro",
"Sidebar": "Barra Lateral",
"Sign In": "Entrar",
"Sign Out": "Sair",
"Sky": "Céu",
"Solarized": "Solarizado",
"Subjects:": "Assuntos:",
"System Fonts": "Fontes do Sistema",
"Theme Color": "Cor do Tema",
"Theme Mode": "Modo do Tema",
"Unknown": "Desconhecido",
@@ -109,8 +102,6 @@
"Updated:": "Atualizado:",
"User avatar": "Avatar do usuário",
"Version {{version}}": "Versão {{version}}",
"Vertical Direction": "Direção Vertical",
"Welcome to your library. You can import your books here and read them anytime.": "Bem-vindo à sua biblioteca. Você pode importar seus livros aqui e lê-los a qualquer momento.",
"Writing Mode": "Modo de Escrita",
"Your Library": "Sua Biblioteca"
}
@@ -2,7 +2,6 @@
"About Readest": "О Readest",
"Add your notes here...": "Добавьте свои заметки здесь...",
"Animation": "Анимация",
"Apply": "Применить",
"Are you sure to delete the selected books?": "Вы уверены, что хотите удалить выбранные книги?",
"Auto Mode": "Автоматический режим",
"Behavior": "Поведение",
@@ -26,7 +25,6 @@
"Edit": "Редактировать",
"Enter your custom CSS here...": "Введите ваш пользовательский CSS здесь...",
"Excerpts": "Отрывки",
"Failed to import book(s): {{filenames}}": "Не удалось импортировать книгу(и): {{filenames}}",
"Font": "Шрифт",
"Font & Layout": "Шрифт и макет",
"Font Face": "Начертание шрифта",
@@ -43,7 +41,6 @@
"Grass": "Трава",
"Gray": "Серый",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Горизонтальное направление",
"Hyphenation": "Перенос слов",
"Identifier:": "Идентификатор:",
"Import Books": "Импорт книг",
@@ -71,7 +68,6 @@
"Notebook": "Блокнот",
"Notes": "Заметки",
"Open": "Открыть",
"Open Book": "Открыть книгу",
"Override Publisher Font": "Переопределить шрифт издателя",
"Page": "Страница",
"Paging Animation": "Анимация перелистывания",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Режим прокрутки",
"Search books...": "Поиск книг...",
"Search...": "Поиск...",
"Select Book": "Выбрать книгу",
"Select books": "Выбрать книги",
"Select multiple books": "Выбрать несколько книг",
"Sepia": "Сепия",
"Serif Font": "Шрифт с засечками",
"Show Book Details": "Показать детали книги",
"Sidebar": "Боковая панель",
"Sign In": "Войти",
"Sign Out": "Выйти",
"Sky": "Небесный",
"Solarized": "Солнечный",
"Subjects:": "Темы:",
"System Fonts": "Системные шрифты",
"Theme Color": "Цвет темы",
"Theme Mode": "Режим темы",
"Unknown": "Неизвестно",
@@ -109,8 +102,6 @@
"Updated:": "Обновлено:",
"User avatar": "Аватар пользователя",
"Version {{version}}": "Версия {{version}}",
"Vertical Direction": "Вертикальное направление",
"Welcome to your library. You can import your books here and read them anytime.": "Добро пожаловать в вашу библиотеку. Вы можете импортировать сюда книги и читать их в любое время.",
"Writing Mode": "Режим записи",
"Your Library": "Ваша библиотека"
}
@@ -2,7 +2,6 @@
"About Readest": "Readest Hakkında",
"Add your notes here...": "Notlarınızı buraya ekleyin...",
"Animation": "Animasyon",
"Apply": "Uygula",
"Are you sure to delete the selected books?": "Seçili kitapları silmek istediğinizden emin misiniz?",
"Auto Mode": "Otomatik Mod",
"Behavior": "Davranış",
@@ -26,7 +25,6 @@
"Edit": "Düzenle",
"Enter your custom CSS here...": "Özel CSS'nizi buraya girin...",
"Excerpts": "Alıntılar",
"Failed to import book(s): {{filenames}}": "Kitap(lar) içe aktarılamadı: {{filenames}}",
"Font": "Yazı Tipi",
"Font & Layout": "Yazı Tipi ve Düzen",
"Font Face": "Yazı Tipi Yüzü",
@@ -43,7 +41,6 @@
"Grass": "Çimen",
"Gray": "Gri",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Yatay Yön",
"Hyphenation": "Heceleme",
"Identifier:": "Tanımlayıcı:",
"Import Books": "Kitapları İçe Aktar",
@@ -71,7 +68,6 @@
"Notebook": "Not Defteri",
"Notes": "Notlar",
"Open": "Aç",
"Open Book": "Kitabı Aç",
"Override Publisher Font": "Yayıncı Yazı Tipini Geçersiz Kıl",
"Page": "Sayfa",
"Paging Animation": "Sayfa Çevirme Animasyonu",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Kaydırma Modu",
"Search books...": "Kitap ara...",
"Search...": "Ara...",
"Select Book": "Kitap Seç",
"Select books": "Kitapları seç",
"Select multiple books": "Birden fazla kitap seç",
"Sepia": "Sepya",
"Serif Font": "Serif Yazı Tipi",
"Show Book Details": "Kitap Detaylarını Göster",
"Sidebar": "Kenar Çubuğu",
"Sign In": "Giriş Yap",
"Sign Out": "Çıkış Yap",
"Sky": "Gökyüzü",
"Solarized": "Solarized",
"Subjects:": "Konular:",
"System Fonts": "Sistem Yazı Tipleri",
"Theme Color": "Tema Rengi",
"Theme Mode": "Tema Modu",
"Unknown": "Bilinmiyor",
@@ -109,8 +102,6 @@
"Updated:": "Güncellendi:",
"User avatar": "Kullanıcı avatarı",
"Version {{version}}": "Sürüm {{version}}",
"Vertical Direction": "Dikey Yön",
"Welcome to your library. You can import your books here and read them anytime.": "Kütüphanenize hoş geldiniz. Buradan kitaplarınızı içe aktarabilir ve istediğiniz zaman okuyabilirsiniz.",
"Writing Mode": "Yazma Modu",
"Your Library": "Kütüphaneniz"
}
@@ -2,7 +2,6 @@
"About Readest": "Về Readest",
"Add your notes here...": "Thêm ghi chú của bạn vào đây...",
"Animation": "Hiệu ứng động",
"Apply": "Áp dụng",
"Are you sure to delete the selected books?": "Bạn có chắc chắn muốn xóa các sách đã chọn?",
"Auto Mode": "Chế độ tự động",
"Behavior": "Hành vi",
@@ -26,7 +25,6 @@
"Edit": "Chỉnh sửa",
"Enter your custom CSS here...": "Nhập CSS tùy chỉnh của bạn vào đây...",
"Excerpts": "Trích dẫn",
"Failed to import book(s): {{filenames}}": "Không thể nhập sách: {{filenames}}",
"Font": "Phông chữ",
"Font & Layout": "Phông chữ & Bố cục",
"Font Face": "Kiểu chữ",
@@ -43,7 +41,6 @@
"Grass": "Xanh cỏ",
"Gray": "Xám",
"Gruvbox": "Gruvbox",
"Horizontal Direction": "Hướng ngang",
"Hyphenation": "Gạch nối từ",
"Identifier:": "Định danh:",
"Import Books": "Nhập sách",
@@ -71,7 +68,6 @@
"Notebook": "Sổ ghi chép",
"Notes": "Ghi chú",
"Open": "Mở",
"Open Book": "Mở sách",
"Override Publisher Font": "Ghi đè phông chữ của nhà xuất bản",
"Page": "Trang",
"Paging Animation": "Hiệu ứng lật trang",
@@ -89,19 +85,16 @@
"Scrolled Mode": "Chế độ cuộn",
"Search books...": "Tìm kiếm sách...",
"Search...": "Tìm kiếm...",
"Select Book": "Chọn sách",
"Select books": "Chọn sách",
"Select multiple books": "Chọn nhiều sách",
"Sepia": "Nâu cổ",
"Serif Font": "Phông chữ có chân",
"Show Book Details": "Hiển thị chi tiết sách",
"Sidebar": "Thanh bên",
"Sign In": "Đăng nhập",
"Sign Out": "Đăng xuất",
"Sky": "Xanh trời",
"Solarized": "Solarized",
"Subjects:": "Chủ đề:",
"System Fonts": "Phông chữ hệ thống",
"Theme Color": "Màu chủ đề",
"Theme Mode": "Chế độ chủ đề",
"Unknown": "Không xác định",
@@ -109,8 +102,6 @@
"Updated:": "Cập nhật:",
"User avatar": "Ảnh đại diện",
"Version {{version}}": "Phiên bản {{version}}",
"Vertical Direction": "Hướng dọc",
"Welcome to your library. You can import your books here and read them anytime.": "Chào mừng đến với thư viện của bạn. Bạn có thể nhập sách vào đây và đọc bất cứ lúc nào.",
"Writing Mode": "Chế độ viết",
"Your Library": "Thư viện của bạn"
}
@@ -2,7 +2,6 @@
"About Readest": "关于 Readest",
"Add your notes here...": "在这里添加您的笔记...",
"Animation": "动画",
"Apply": "应用",
"Are you sure to delete the selected books?": "您确定要删除所选书籍吗?",
"Auto Mode": "自动主题",
"Behavior": "行为",
@@ -26,7 +25,6 @@
"Edit": "编辑",
"Enter your custom CSS here...": "在此处输入您的自定义 CSS...",
"Excerpts": "摘录",
"Failed to import book(s): {{filenames}}": "导入书籍失败:{{filenames}}",
"Font": "字体",
"Font & Layout": "字体和布局",
"Font Face": "字型",
@@ -43,7 +41,6 @@
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
"Horizontal Direction": "横排",
"Hyphenation": "断字",
"Identifier:": "识别码",
"Import Books": "导入书籍",
@@ -71,9 +68,8 @@
"Notebook": "笔记本",
"Notes": "笔记",
"Open": "打开",
"Open Book": "打开书籍",
"Override Publisher Font": "覆盖内置字体",
"Page": "页",
"Page": "页",
"Paging Animation": "翻页动画",
"Paragraph": "段落",
"Parallel Read": "并排阅读",
@@ -89,19 +85,16 @@
"Scrolled Mode": "滚动模式",
"Search books...": "搜索书籍...",
"Search...": "搜索...",
"Select Book": "选择书籍",
"Select books": "选择书籍",
"Select multiple books": "选择多本书籍",
"Sepia": "旧韵",
"Serif Font": "衬线字体",
"Show Book Details": "显示书籍详情",
"Sidebar": "侧边栏",
"Sign In": "登录",
"Sign Out": "登出",
"Sky": "天青",
"Solarized": "日晖",
"Subjects:": "主题",
"System Fonts": "系统字体",
"Theme Color": "主题颜色",
"Theme Mode": "主题模式",
"Unknown": "未知",
@@ -109,8 +102,6 @@
"Updated:": "更新日期",
"User avatar": "用户头像",
"Version {{version}}": "版本 {{version}}",
"Vertical Direction": "竖排",
"Welcome to your library. You can import your books here and read them anytime.": "书库空空如也,您可以导入您的书籍并随时阅读。",
"Writing Mode": "排版模式",
"Your Library": "书库"
}
@@ -2,7 +2,6 @@
"About Readest": "關於 Readest",
"Add your notes here...": "在這裡添加您的筆記...",
"Animation": "動畫",
"Apply": "應用",
"Are you sure to delete the selected books?": "您確定要刪除所選書籍嗎?",
"Auto Mode": "自動主題",
"Behavior": "行為",
@@ -26,7 +25,6 @@
"Edit": "編輯",
"Enter your custom CSS here...": "在此處輸入您的自定義 CSS...",
"Excerpts": "摘錄",
"Failed to import book(s): {{filenames}}": "導入書籍失敗:{{filenames}}",
"Font": "字體",
"Font & Layout": "字體和版面",
"Font Face": "字型",
@@ -43,7 +41,6 @@
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
"Horizontal Direction": "橫排",
"Hyphenation": "斷字",
"Identifier:": "識別碼",
"Import Books": "導入書籍",
@@ -71,9 +68,8 @@
"Notebook": "筆記本",
"Notes": "筆記",
"Open": "打開",
"Open Book": "打開書籍",
"Override Publisher Font": "覆蓋內置字體",
"Page": "頁",
"Page": "頁",
"Paging Animation": "翻頁動畫",
"Paragraph": "段落",
"Parallel Read": "並排閱讀",
@@ -89,19 +85,16 @@
"Scrolled Mode": "滾動模式",
"Search books...": "搜索書籍...",
"Search...": "搜索...",
"Select Book": "選擇書籍",
"Select books": "選擇書籍",
"Select multiple books": "選擇多本書籍",
"Sepia": "舊韻",
"Serif Font": "襯線字體",
"Show Book Details": "顯示書籍詳情",
"Sidebar": "側邊欄",
"Sign In": "登入",
"Sign Out": "登出",
"Sky": "天青",
"Solarized": "日暉",
"Subjects:": "主題",
"System Fonts": "系統字體",
"Theme Color": "主題顏色",
"Theme Mode": "主題模式",
"Unknown": "未知",
@@ -109,8 +102,6 @@
"Updated:": "更新日期",
"User avatar": "用戶頭像",
"Version {{version}}": "版本 {{version}}",
"Vertical Direction": "豎排",
"Welcome to your library. You can import your books here and read them anytime.": "書庫空空如也,您可以導入您的書籍並隨時閱讀。",
"Writing Mode": "排版模式",
"Your Library": "書庫"
}
+1 -17
View File
@@ -1,21 +1,5 @@
{
"releases": {
"0.9.0": {
"date": "2025-01-04",
"notes": [
"Better Custom CSS editor and OAuth process.",
"Add vertical/horizontal switch for CJK books.",
"Fix dropdown close overlay not working on Windows/Linux."
]
},
"0.8.9": {
"date": "2025-01-02",
"notes": [
"Context menu on bookshelf for quick actions.",
"Add more system fonts as custom fonts.",
"Fix google and github login for native apps."
]
},
"0.8.8": {
"date": "2024-12-29",
"notes": [
@@ -60,7 +44,7 @@
"notes": [
"Support file associations for one-click open with Readest.",
"Support APP auto updater.",
"Support online web version."
"Suppport online web version."
]
},
"0.7.9": {
+3 -137
View File
@@ -15,7 +15,6 @@ dependencies = [
"tauri",
"tauri-build 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tauri-plugin-cli",
"tauri-plugin-deep-link",
"tauri-plugin-devtools",
"tauri-plugin-dialog",
"tauri-plugin-fs",
@@ -26,7 +25,6 @@ dependencies = [
"tauri-plugin-os",
"tauri-plugin-process",
"tauri-plugin-shell",
"tauri-plugin-single-instance",
"tauri-plugin-updater",
]
@@ -893,26 +891,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom 0.2.15",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -1055,12 +1033,6 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -1305,15 +1277,6 @@ dependencies = [
"syn 2.0.91",
]
[[package]]
name = "dlv-list"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]]
name = "document-features"
version = "0.2.10"
@@ -2053,12 +2016,6 @@ dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.2"
@@ -3267,16 +3224,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-multimap"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
dependencies = [
"dlv-list",
"hashbrown 0.14.5",
]
[[package]]
name = "ordered-stream"
version = "0.2.0"
@@ -4057,7 +4004,7 @@ dependencies = [
"wasm-streams",
"web-sys",
"webpki-roots",
"windows-registry 0.2.0",
"windows-registry",
]
[[package]]
@@ -4137,17 +4084,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "rust-ini"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f"
dependencies = [
"cfg-if",
"ordered-multimap",
"trim-in-place",
]
[[package]]
name = "rust_decimal"
version = "1.36.0"
@@ -5022,26 +4958,6 @@ dependencies = [
"thiserror 2.0.9",
]
[[package]]
name = "tauri-plugin-deep-link"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35d51ffd286073414d26353bcfc9e83e3cd63f96fa7f7a912f92f2118e5de5a6"
dependencies = [
"dunce",
"rust-ini",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 2.0.9",
"tracing",
"url",
"windows-registry 0.3.0",
"windows-result",
]
[[package]]
name = "tauri-plugin-devtools"
version = "2.0.0"
@@ -5240,21 +5156,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tauri-plugin-single-instance"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f36019ee9832dc99e4450bb55a21cfad8633b19c2c18bd17c7741939b070ede"
dependencies = [
"serde",
"serde_json",
"tauri",
"thiserror 2.0.9",
"tracing",
"windows-sys 0.59.0",
"zbus 4.4.0",
]
[[package]]
name = "tauri-plugin-updater"
version = "2.3.0"
@@ -5521,15 +5422,6 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "tinystr"
version = "0.7.6"
@@ -5891,12 +5783,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "trim-in-place"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -6446,7 +6332,7 @@ dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings 0.1.0",
"windows-strings",
"windows-targets 0.52.6",
]
@@ -6479,18 +6365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
dependencies = [
"windows-result",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-registry"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a"
dependencies = [
"windows-result",
"windows-strings 0.2.0",
"windows-strings",
"windows-targets 0.52.6",
]
@@ -6513,15 +6388,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978d65aedf914c664c510d9de43c8fd85ca745eaff1ed53edf409b479e441663"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
+1 -3
View File
@@ -6,7 +6,7 @@ authors = ["Bilingify LLC"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.77.2"
rust-version = "1.71"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -34,7 +34,6 @@ tauri-plugin-shell = "2"
tauri-plugin-process = "2"
tauri-plugin-oauth = "2"
tauri-plugin-opener = "2.2.2"
tauri-plugin-deep-link = "2"
[patch.crates-io]
tauri = { path = "../../../packages/tauri/crates/tauri" }
@@ -45,5 +44,4 @@ rand = "0.8"
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
tauri-plugin-cli = "2"
tauri-plugin-single-instance = "2.2.0"
tauri-plugin-updater = "2"
@@ -55,12 +55,13 @@
"core:window:allow-start-dragging",
"core:window:allow-toggle-maximize",
"shell:default",
"updater:default",
"process:default",
"process:allow-exit",
"process:allow-restart",
"cli:default",
"oauth:allow-start",
"oauth:allow-cancel",
"opener:default",
"deep-link:default"
"opener:default"
]
}
@@ -1,10 +0,0 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": [
"updater:default",
"cli:default"
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

+6 -46
View File
@@ -70,16 +70,11 @@ async fn start_server(window: Window) -> Result<u16, String> {
.map_err(|err| err.to_string())
}
#[derive(Clone, serde::Serialize)]
struct Payload {
args: Vec<String>,
cwd: String,
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let builder = tauri::Builder::default()
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_oauth::init())
.invoke_handler(tauri::generate_handler![start_server])
.plugin(tauri_plugin_shell::init())
@@ -89,20 +84,6 @@ pub fn run() {
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init());
#[cfg(desktop)]
let builder = builder.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
let _ = app
.get_webview_window("main")
.expect("no main window")
.set_focus();
app.emit("single-instance", Payload { args: argv, cwd }).unwrap();
}));
let builder = builder.plugin(tauri_plugin_deep_link::init());
#[cfg(desktop)]
let builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
#[cfg(target_os = "macos")]
let builder = builder.plugin(tauri_traffic_light_positioner_plugin::init());
@@ -113,7 +94,7 @@ pub fn run() {
let mut files = Vec::new();
// NOTICE: `args` may include URL protocol (`your-app-protocol://`)
// or arguments (`--`) if your app supports them.
// files may also be passed as `file://path/to/file`
// files may aslo be passed as `file://path/to/file`
for maybe_file in std::env::args().skip(1) {
// skip flags like -f or --flag
if maybe_file.starts_with("-") {
@@ -139,25 +120,8 @@ pub fn run() {
});
}
}
#[cfg(desktop)]
{
app.handle().plugin(tauri_plugin_cli::init())?;
let app_handle = app.handle().clone();
app.listen("window-ready", move |_| {
app_handle.get_webview_window("main").unwrap()
.eval("window.__READEST_CLI_ACCESS = true; window.__READEST_UPDATER_ACCESS = true;")
.expect("Failed to set cli access config");
});
}
#[cfg(any(windows, target_os = "linux"))]
{
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?;
}
app.handle().plugin(tauri_plugin_cli::init())?;
#[cfg(desktop)]
if cfg!(debug_assertions) {
app.handle().plugin(
@@ -166,11 +130,7 @@ pub fn run() {
.build(),
)?;
}
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default());
#[cfg(desktop)]
let win_builder = win_builder
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
.inner_size(800.0, 600.0)
.resizable(true)
.maximized(true);
@@ -181,7 +141,7 @@ pub fn run() {
.title_bar_style(TitleBarStyle::Overlay)
.title("");
#[cfg(all(not(target_os = "macos"), desktop))]
#[cfg(not(target_os = "macos"))]
let win_builder = win_builder
.decorations(false)
.transparent(true)
@@ -203,7 +163,7 @@ pub fn run() {
.run(
#[allow(unused_variables)]
|app_handle, event| {
#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "ios"))]
if let tauri::RunEvent::Opened { urls } = event {
let files = urls
.into_iter()
@@ -130,12 +130,6 @@
}
]
},
"deep-link": {
"mobile": [{ "host": "web.readest.com" }],
"desktop": {
"schemes": ["readest"]
}
},
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU0OTAxMURGQkUzQjFENTQKUldSVUhUdSszeEdRNUExdmFkWnlvYWNYNG5wamkxMmUxRk5SejlMOTJVd28yNXlVTDh6Wld4OC8K",
"endpoints": ["https://github.com/readest/readest/releases/latest/download/latest.json"]
+24 -56
View File
@@ -17,18 +17,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 { start, cancel, onUrl, onInvalidUrl } from '@fabianlars/tauri-plugin-oauth';
import { openUrl } from '@tauri-apps/plugin-opener';
import { handleAuthCallback } from '@/helpers/auth';
type OAuthProvider = 'google' | 'apple' | 'azure' | 'github';
interface SingleInstancePayload {
args: string[];
cwd: string;
}
interface ProviderLoginProp {
provider: OAuthProvider;
handleSignIn: (provider: OAuthProvider) => void;
@@ -70,10 +64,7 @@ export default function AuthPage() {
provider,
options: {
skipBrowserRedirect: true,
redirectTo:
process.env.NODE_ENV === 'production'
? 'readest://auth/callback'
: `http://localhost:${port}`,
redirectTo: `http://localhost:${port}`,
},
});
@@ -84,54 +75,34 @@ export default function AuthPage() {
openUrl(data.url);
};
const handleOAuthUrl = async (url: string) => {
console.log('Received OAuth URL:', url);
const hashMatch = url.match(/#(.*)/);
if (hashMatch) {
const hash = hashMatch[1];
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
const refreshToken = params.get('refresh_token');
const next = params.get('next') ?? '/';
if (accessToken) {
handleAuthCallback({ accessToken, refreshToken, next, login, navigate: router.push });
}
}
};
const startTauriOAuth = async () => {
const startOAuthServer = async () => {
try {
if (process.env.NODE_ENV === 'production') {
const { getCurrentWindow } = await import('@tauri-apps/api/window');
const currentWindow = getCurrentWindow();
currentWindow.listen('single-instance', ({ event, payload }) => {
console.log('Received deep link:', event, payload);
const { args } = payload as SingleInstancePayload;
if (args?.[1]) {
handleOAuthUrl(args[1]);
}
});
await onOpenUrl((urls) => {
urls.forEach((url) => {
handleOAuthUrl(url);
});
});
} else {
const port = await start();
setPort(port);
console.log(`OAuth server started on port ${port}`);
const port = await start();
setPort(port);
console.log(`OAuth server started on port ${port}`);
await onUrl(handleOAuthUrl);
await onInvalidUrl((url) => {
console.log('Received invalid OAuth URL:', url);
});
}
await onUrl((url) => {
console.log('Received OAuth URL:', url);
const hashMatch = url.match(/#(.*)/);
if (hashMatch) {
const hash = hashMatch[1];
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
const refreshToken = params.get('refresh_token');
const next = params.get('next') ?? '/';
handleAuthCallback({ accessToken, refreshToken, next, login, navigate: router.push });
}
});
await onInvalidUrl((url) => {
console.log('Received invalid OAuth URL:', url);
});
} catch (error) {
console.error('Error starting OAuth server:', error);
}
};
const stopTauriOAuth = async () => {
const stopOAuthServer = async () => {
try {
if (port) {
await cancel(port);
@@ -155,10 +126,10 @@ export default function AuthPage() {
if (isOAuthServerRunning.current) return;
isOAuthServerRunning.current = true;
startTauriOAuth();
startOAuthServer();
return () => {
isOAuthServerRunning.current = false;
stopTauriOAuth();
stopOAuthServer();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -185,9 +156,6 @@ export default function AuthPage() {
return null;
}
// For tauri app development, use a custom OAuth server to handle the OAuth callback
// For tauri app production, use deeplink to handle the OAuth callback
// For web app, use the built-in OAuth callback page /auth/callback
return isTauriAppPlatform() ? (
<div className='flex pt-11'>
<button
@@ -20,10 +20,10 @@ 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, isWebAppPlatform } from '@/services/environment';
import Alert from '@/components/Alert';
import Spinner from '@/components/Spinner';
import { isTauriAppPlatform } from '@/services/environment';
import BookDetailModal from '@/components/BookDetailModal';
type BookshelfItem = Book | BooksGroup;
@@ -71,14 +71,16 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
const [clickedImage, setClickedImage] = useState<string | null>(null);
const [importBookUrl] = useState(searchParams?.get('url') || '');
const isImportingBook = useRef(false);
const [showDetailsBook, setShowDetailsBook] = useState<Book | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedBook, setSelectedBook] = useState<Book | null>(null);
const showBookDetailsModal = (book: Book) => {
setShowDetailsBook(book);
const showMoreDetails = (book: Book) => {
setIsModalOpen(true);
setSelectedBook(book);
};
const dismissBookDetailsModal = () => {
setShowDetailsBook(null);
const closeModal = () => {
setIsModalOpen(false);
};
const { setLibrary } = useLibraryStore();
@@ -152,12 +154,6 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
const osPlatform = getOSPlatform();
const fileRevealLabel =
FILE_REVEAL_LABELS[osPlatform as FILE_REVEAL_PLATFORMS] || FILE_REVEAL_LABELS.default;
const openBookMenuItem = await MenuItem.new({
text: isSelectMode ? _('Select Book') : _('Open Book'),
action: async () => {
handleBookClick(book.hash);
},
});
const showBookInFinderMenuItem = await MenuItem.new({
text: _(fileRevealLabel),
action: async () => {
@@ -165,12 +161,6 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
revealItemInDir(folder);
},
});
const showBookDetailsMenuItem = await MenuItem.new({
text: _('Show Book Details'),
action: async () => {
showBookDetailsModal(book);
},
});
const deleteBookMenuItem = await MenuItem.new({
text: _('Delete'),
action: async () => {
@@ -178,8 +168,6 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
},
});
const menu = await Menu.new();
menu.append(openBookMenuItem);
menu.append(showBookDetailsMenuItem);
menu.append(showBookInFinderMenuItem);
menu.append(deleteBookMenuItem);
menu.popup();
@@ -191,7 +179,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
{bookshelfItems.map((item, index) => (
<div
key={`library-item-${index}`}
className='hover:bg-base-300/50 group flex h-full flex-col p-4'
className='hover:bg-base-300/50 flex h-full flex-col p-4'
>
<div className='flex-grow'>
{'format' in item ? (
@@ -242,19 +230,17 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
)}
</div>
</div>
<div className='flex flex-row items-center justify-between p-0 pt-2'>
<div className='card-body flex flex-row items-center justify-between p-0 pt-2'>
<h4 className='card-title line-clamp-1 text-[0.6em] text-xs font-semibold'>
{(item as Book).title}
</h4>
{isWebAppPlatform() && (
<div
className='show-detail-button self-start opacity-0 group-hover:opacity-100'
role='button'
onClick={showBookDetailsModal.bind(null, item as Book)}
>
<CiCircleMore size={15} />
</div>
)}
<div
className='card-detail'
role='button'
onClick={showMoreDetails.bind(null, item as Book)}
>
<CiCircleMore size={15} />
</div>
</div>
</div>
) : (
@@ -315,12 +301,8 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
/>
)}
{showDetailsBook && (
<BookDetailModal
isOpen={!!showDetailsBook}
book={showDetailsBook}
onClose={dismissBookDetailsModal}
/>
{selectedBook && (
<BookDetailModal isOpen={isModalOpen} book={selectedBook} onClose={closeModal} />
)}
</div>
);
@@ -71,7 +71,7 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
</div>
<ul
tabIndex={-1}
className='dropdown-content dropdown-center bg-base-100 menu rounded-box z-[1] mt-3 w-52 p-2 shadow'
className='dropdown-content dropdown-center menu rounded-box z-[1] mt-3 w-52 p-2 shadow'
>
<li>
<button className='text-base-content' onClick={onImportBooks}>
+7 -44
View File
@@ -7,9 +7,8 @@ import { useRouter } from 'next/navigation';
import { Book } from '@/types/book';
import { AppService } from '@/types/system';
import { navigateToReader } from '@/utils/nav';
import { getBaseFilename, listFormater } from '@/utils/book';
import { parseOpenWithFiles } from '@/helpers/cli';
import { isTauriAppPlatform, hasUpdater } from '@/services/environment';
import { isTauriAppPlatform } from '@/services/environment';
import { checkForAppUpdates } from '@/helpers/updater';
import { FILE_ACCEPT_FORMATS, SUPPORTED_FILE_EXTS } from '@/services/constants';
@@ -25,7 +24,6 @@ import Spinner from '@/components/Spinner';
import LibraryHeader from './components/LibraryHeader';
import Bookshelf from './components/Bookshelf';
import { AboutWindow } from '@/components/AboutWindow';
import Toast from '@/components/Toast';
const LibraryPage = () => {
const router = useRouter();
@@ -46,19 +44,9 @@ const LibraryPage = () => {
const [isSelectMode, setIsSelectMode] = useState(false);
const demoBooks = useDemoBooks();
const [toastMessage, setToastMessage] = useState('');
const toastDismissTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
if (toastDismissTimeout.current) clearTimeout(toastDismissTimeout.current);
toastDismissTimeout.current = setTimeout(() => setToastMessage(''), 5000);
return () => {
if (toastDismissTimeout.current) clearTimeout(toastDismissTimeout.current);
};
}, [toastMessage]);
useEffect(() => {
const doAppUpdates = async () => {
if (hasUpdater()) {
if (isTauriAppPlatform()) {
await checkForAppUpdates();
}
};
@@ -69,14 +57,9 @@ const LibraryPage = () => {
async (appService: AppService, openWithFiles: string[], libraryBooks: Book[]) => {
const bookIds: string[] = [];
for (const file of openWithFiles) {
console.log('Open with book:', file);
try {
const book = await appService.importBook(file, libraryBooks);
if (book) {
bookIds.push(book.hash);
}
} catch (error) {
console.log('Failed to import book:', file, error);
const book = await appService.importBook(file, libraryBooks);
if (book) {
bookIds.push(book.hash);
}
}
setLibrary(libraryBooks);
@@ -166,22 +149,9 @@ const LibraryPage = () => {
const importBooks = async (files: [string | File]) => {
setLoading(true);
const failedFiles = [];
for (const file of files) {
try {
await appService?.importBook(file, libraryBooks);
setLibrary(libraryBooks);
} catch (error) {
const filename = typeof file === 'string' ? file : file.name;
const baseFilename = getBaseFilename(filename);
failedFiles.push(baseFilename);
setToastMessage(
_('Failed to import book(s): {{filenames}}', {
filenames: listFormater(false).format(failedFiles),
}),
);
console.error('Failed to import book:', filename, error);
}
await appService?.importBook(file, libraryBooks);
setLibrary(libraryBooks);
}
appService?.saveLibraryBooks(libraryBooks);
setLoading(false);
@@ -283,13 +253,6 @@ const LibraryPage = () => {
</div>
))}
<AboutWindow />
{toastMessage && (
<Toast
message={toastMessage}
toastClass='toast-top toast-end pt-11'
alertClass='alert-error max-w-80'
/>
)}
</div>
);
};
@@ -28,7 +28,6 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const { getView, getViewSettings } = useReaderStore();
const { themeCode } = useTheme();
const view = getView(bookKey);
const viewSettings = getViewSettings(bookKey)!;
const footnoteHandler = new FootnoteHandler();
useEffect(() => {
@@ -74,13 +73,7 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const rect = gridFrame.getBoundingClientRect();
const viewSettings = getViewSettings(bookKey)!;
const triangPos = getPosition(detail.a, rect, viewSettings.vertical);
const popupPos = getPopupPosition(
triangPos,
rect,
viewSettings.vertical ? popupHeight : popupWidth,
viewSettings.vertical ? popupWidth : popupHeight,
popupPadding,
);
const popupPos = getPopupPosition(triangPos, rect, popupWidth, popupHeight, popupPadding);
setTrianglePosition(triangPos);
setPopupPosition(popupPos);
@@ -114,9 +107,6 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
}
}, [footnoteRef]);
const width = viewSettings.vertical ? popupHeight : popupWidth;
const height = viewSettings.vertical ? popupWidth : popupHeight;
return (
<div>
{showPopup && (
@@ -127,8 +117,8 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
/>
)}
<Popup
width={width}
height={height}
width={popupWidth}
height={popupHeight}
position={showPopup ? popupPosition! : undefined}
trianglePosition={showPopup ? trianglePosition! : undefined}
className='select-text overflow-y-auto'
@@ -137,8 +127,8 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
className=''
ref={footnoteRef}
style={{
width: `${width}px`,
height: `${height}px`,
width: `${popupWidth}px`,
height: `${popupHeight}px`,
}}
></div>
</Popup>
@@ -9,23 +9,21 @@ import { useSettingsStore } from '@/store/settingsStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useReaderStore } from '@/store/readerStore';
import { useSidebarStore } from '@/store/sidebarStore';
import { Book } from '@/types/book';
import { SystemSettings } from '@/types/settings';
import { parseOpenWithFiles } from '@/helpers/cli';
import { tauriHandleClose, tauriHandleOnCloseWindow } from '@/utils/window';
import { isTauriAppPlatform } from '@/services/environment';
import { uniqueId } from '@/utils/misc';
import { eventDispatcher } from '@/utils/event';
import { navigateToLibrary } from '@/utils/nav';
import { BOOK_IDS_SEPARATOR } from '@/services/constants';
import useBooksManager from '../hooks/useBooksManager';
import useBookShortcuts from '../hooks/useBookShortcuts';
import BookDetailModal from '@/components/BookDetailModal';
import Spinner from '@/components/Spinner';
import SideBar from './sidebar/SideBar';
import Notebook from './notebook/Notebook';
import BooksGrid from './BooksGrid';
import { eventDispatcher } from '@/utils/event';
const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ ids, settings }) => {
const router = useRouter();
@@ -37,7 +35,6 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
const { getConfig, getBookData, saveConfig } = useBookDataStore();
const { getView, setBookKeys } = useReaderStore();
const { initViewState, getViewState, clearViewState } = useReaderStore();
const [showDetailsBook, setShowDetailsBook] = useState<Book | null>(null);
const isInitiating = useRef(false);
const [loading, setLoading] = useState(false);
@@ -62,13 +59,6 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
if (index === 0) setSideBarBookKey(key);
}
});
const handleShowBookDetails = (event: CustomEvent) => {
const book = event.detail as Book;
setShowDetailsBook(book);
return true;
};
eventDispatcher.onSync('show-book-details', handleShowBookDetails);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -88,19 +78,14 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
const { book } = getBookData(bookKey) || {};
const { isPrimary } = getViewState(bookKey) || {};
if (isPrimary && book && config) {
const settings = useSettingsStore.getState().settings;
await saveConfig(envConfig, bookKey, config, settings);
}
};
const saveConfigAndCloseBook = async (bookKey: string) => {
console.log('Closing book', bookKey);
try {
getView(bookKey)?.close();
getView(bookKey)?.remove();
} catch {
console.info('Error closing book', bookKey);
}
getView(bookKey)?.close();
getView(bookKey)?.remove();
await saveBookConfig(bookKey);
clearViewState(bookKey);
};
@@ -111,7 +96,6 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
};
const handleCloseBooks = async () => {
const settings = useSettingsStore.getState().settings;
await Promise.all(bookKeys.map((key) => saveConfigAndCloseBook(key)));
await saveSettings(envConfig, settings);
};
@@ -155,13 +139,6 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
<SideBar onGoToLibrary={handleCloseBooksToLibrary} />
<BooksGrid bookKeys={bookKeys} onCloseBook={handleCloseBook} />
<Notebook />
{showDetailsBook && (
<BookDetailModal
isOpen={!!showDetailsBook}
book={showDetailsBook}
onClose={() => setShowDetailsBook(null)}
/>
)}
</div>
);
};
@@ -86,7 +86,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
return (
<div
tabIndex={0}
className='view-menu dropdown-content bgcolor-base-200 dropdown-right no-triangle border-base-200 z-20 mt-1 w-72 border shadow-2xl'
className='view-menu dropdown-content dropdown-right no-triangle border-base-100 z-20 mt-1 w-72 border shadow-2xl'
>
<div className={clsx('flex items-center justify-between rounded-md')}>
<button
@@ -6,7 +6,7 @@ import { TextSelection } from '@/utils/sel';
import { BookNote } from '@/types/book';
interface NoteEditorProps {
onSave: (selection: TextSelection, note: string) => void;
onSave: (selction: TextSelection, note: string) => void;
onEdit: (annotation: BookNote) => void;
}
@@ -57,7 +57,7 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ onSave, onEdit }) => {
className={clsx(
'note-editor textarea textarea-ghost min-h-[1em] resize-none !outline-none',
'inset-0 w-full rounded-none border-0 bg-transparent p-0 leading-normal',
'text-sm',
'text-base',
)}
ref={editorRef}
value={note}
@@ -70,7 +70,7 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ onSave, onEdit }) => {
<button
className={clsx(
'btn btn-ghost settings-content hover:bg-transparent',
'flex h-[1.3em] min-h-[1.3em] items-end p-0',
'flex h-[1.5em] min-h-[1.5em] items-end p-0',
editorRef.current && editorRef.current.value ? '' : 'btn-disabled !bg-opacity-0',
)}
onClick={handleSaveNote}
@@ -160,15 +160,21 @@ const Notebook: React.FC = ({}) => {
>
<p className='line-clamp-1'>{item.text || `Excerpt ${index + 1}`}</p>
</div>
<div className='collapse-content select-text px-3 pb-0 text-xs'>
<div
className='collapse-content select-text px-3 pb-0 text-xs'
onClick={(e) => e.stopPropagation()}
>
<p className='hyphens-auto text-justify'>{item.text}</p>
<div className='flex justify-end'>
<div
className='cursor-pointer align-bottom text-xs text-red-400'
<button
className={clsx(
'btn btn-ghost settings-content hover:bg-transparent',
'flex h-[1em] min-h-[1em] items-end p-0',
)}
onClick={handleEditNote.bind(null, item, true)}
>
{_('Delete')}
</div>
<div className='align-bottom text-xs text-red-400'>{_('Delete')}</div>
</button>
</div>
</div>
</div>
@@ -1,14 +1,11 @@
import clsx from 'clsx';
import React from 'react';
import { FiChevronUp, FiChevronLeft } from 'react-icons/fi';
import { FiChevronDown } from 'react-icons/fi';
import { MdCheck } from 'react-icons/md';
import { useTranslation } from '@/hooks/useTranslation';
interface DropdownProps {
family?: string;
selected: string;
options: string[];
moreOptions?: string[];
onSelect: (option: string) => void;
onGetFontFamily: (option: string, family: string) => string;
}
@@ -17,23 +14,21 @@ const FontDropdown: React.FC<DropdownProps> = ({
family,
selected,
options,
moreOptions,
onSelect,
onGetFontFamily,
}) => {
const _ = useTranslation();
return (
<div className='dropdown dropdown-top'>
<div className='dropdown dropdown-end'>
<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>
<FiChevronUp className='h-4 w-4' />
<FiChevronDown className='h-4 w-4' />
</button>
<ul
tabIndex={0}
className='dropdown-content bgcolor-base-200 no-triangle menu rounded-box absolute right-0 z-[1] mt-4 w-44 shadow'
className='dropdown-content dropdown-right menu bg-base-100 rounded-box z-[1] mt-4 w-44 shadow'
>
{options.map((option) => (
<li key={option} onClick={() => onSelect(option)}>
@@ -45,36 +40,6 @@ const FontDropdown: React.FC<DropdownProps> = ({
</div>
</li>
))}
{moreOptions && moreOptions.length > 0 && (
<li className='dropdown dropdown-left dropdown-top'>
<div className='flex items-center px-0'>
<span style={{ minWidth: '20px' }}>
<FiChevronLeft className='h-4 w-4' />
</span>
<span>{_('System Fonts')}</span>
</div>
<ul
tabIndex={0}
className={clsx(
'dropdown-content bgcolor-base-200 menu rounded-box relative z-[1] overflow-y-scroll shadow',
'!mr-5 mb-[-46px] max-h-80 w-[292px]',
)}
>
{moreOptions.map((option) => (
<li key={option} onClick={() => onSelect(option)}>
<div className='flex items-center px-0'>
<span style={{ minWidth: '20px' }}>
{selected === option && <MdCheck size={20} className='text-base-content' />}
</span>
<span style={{ fontFamily: onGetFontFamily(option, family ?? '') }}>
{option}
</span>
</div>
</li>
))}
</ul>
</li>
)}
</ul>
</div>
);
@@ -3,27 +3,18 @@ import React, { useEffect, useState } from 'react';
import NumberInput from './NumberInput';
import FontDropdown from './FontDropDown';
import {
LINUX_FONTS,
MACOS_FONTS,
MONOSPACE_FONTS,
SANS_SERIF_FONTS,
SERIF_FONTS,
WINDOWS_FONTS,
} from '@/services/constants';
import { MONOSPACE_FONTS, SANS_SERIF_FONTS, SERIF_FONTS } from '@/services/constants';
import { useReaderStore } from '@/store/readerStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useTheme } from '@/hooks/useTheme';
import { getStyles } from '@/utils/style';
import { getOSPlatform } from '@/utils/misc';
interface FontFaceProps {
className?: string;
family: string;
label: string;
options: string[];
moreOptions?: string[];
selected: string;
onSelect: (option: string) => void;
}
@@ -34,21 +25,12 @@ const handleFontFaceFont = (option: string, family: string) => {
return `'${option}', ${family}`;
};
const FontFace = ({
className,
family,
label,
options,
moreOptions,
selected,
onSelect,
}: FontFaceProps) => (
const FontFace = ({ className, family, label, options, selected, onSelect }: FontFaceProps) => (
<div className={clsx('config-item', className)}>
<span className=''>{label}</span>
<FontDropdown
family={family}
options={options}
moreOptions={moreOptions}
selected={selected}
onSelect={onSelect}
onGetFontFamily={handleFontFaceFont}
@@ -64,21 +46,6 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const viewSettings = getViewSettings(bookKey)!;
const { themeCode } = useTheme();
const osPlatform = getOSPlatform();
let moreFonts: string[] = [];
switch (osPlatform) {
case 'macos':
moreFonts = MACOS_FONTS;
break;
case 'windows':
moreFonts = WINDOWS_FONTS;
break;
case 'linux':
moreFonts = LINUX_FONTS;
break;
default:
break;
}
const [defaultFontSize, setDefaultFontSize] = useState(viewSettings.defaultFontSize!);
const [minFontSize, setMinFontSize] = useState(viewSettings.minimumFontSize!);
const [overrideFont, setOverrideFont] = useState(viewSettings.overrideFont!);
@@ -239,7 +206,6 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
family='serif'
label={_('Serif Font')}
options={SERIF_FONTS}
moreOptions={moreFonts}
selected={serifFont}
onSelect={setSerifFont}
/>
@@ -247,7 +213,6 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
family='sans-serif'
label={_('Sans-Serif Font')}
options={SANS_SERIF_FONTS}
moreOptions={moreFonts}
selected={sansSerifFont}
onSelect={setSansSerifFont}
/>
@@ -256,7 +221,6 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
family='monospace'
label={_('Monospace Font')}
options={MONOSPACE_FONTS}
moreOptions={moreFonts}
selected={monospaceFont}
onSelect={setMonospaceFont}
/>
@@ -1,24 +1,17 @@
import React, { useEffect, useState } from 'react';
import { MdOutlineAutoMode } from 'react-icons/md';
import { MdOutlineTextRotationDown, MdOutlineTextRotationNone } from 'react-icons/md';
import { ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
import { useSettingsStore } from '@/store/settingsStore';
import { useReaderStore } from '@/store/readerStore';
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 { ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
import NumberInput from './NumberInput';
import { useTheme } from '@/hooks/useTheme';
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const _ = useTranslation();
const { settings, isFontLayoutSettingsGlobal, setSettings } = useSettingsStore();
const { getView, getViewSettings, setViewSettings } = useReaderStore();
const { getBookData } = useBookDataStore();
const view = getView(bookKey);
const bookData = getBookData(bookKey)!;
const viewSettings = getViewSettings(bookKey)!;
const { themeCode } = useTheme();
@@ -30,7 +23,6 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [maxColumnCount, setMaxColumnCount] = useState(viewSettings.maxColumnCount!);
const [maxInlineSize, setMaxInlineSize] = useState(viewSettings.maxInlineSize!);
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize!);
const [writingMode, setWritingMode] = useState(viewSettings.writingMode!);
useEffect(() => {
viewSettings.lineHeight = lineHeight;
@@ -119,55 +111,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [maxInlineSize]);
useEffect(() => {
// global settings are not supported for writing mode
viewSettings.writingMode = writingMode;
setViewSettings(bookKey, viewSettings);
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [writingMode]);
const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language);
const isCJKBook = langCode === 'zh' || langCode === 'ja' || langCode === 'ko';
return (
<div className='my-4 w-full space-y-6'>
{isCJKBook && (
<div className='w-full'>
<div className='flex items-center justify-between'>
<h2 className='font-medium'>{_('Writing Mode')}</h2>
<div className='flex gap-2'>
<div className='tooltip tooltip-bottom' data-tip={_('Default')}>
<button
className={`btn btn-ghost btn-circle ${writingMode === 'auto' ? 'btn-active bg-base-300' : ''}`}
onClick={() => setWritingMode('auto')}
>
<MdOutlineAutoMode size={20} />
</button>
</div>
<div className='tooltip tooltip-bottom' data-tip={_('Horizontal Direction')}>
<button
className={`btn btn-ghost btn-circle ${writingMode === 'horizontal-tb' ? 'btn-active bg-base-300' : ''}`}
onClick={() => setWritingMode('horizontal-tb')}
>
<MdOutlineTextRotationNone size={20} />
</button>
</div>
<div className='tooltip tooltip-bottom' data-tip={_('Vertical Direction')}>
<button
className={`btn btn-ghost btn-circle ${writingMode === 'vertical-rl' ? 'btn-active bg-base-300' : ''}`}
onClick={() => setWritingMode('vertical-rl')}
>
<MdOutlineTextRotationDown size={20} />
</button>
</div>
</div>
</div>
</div>
)}
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Paragraph')}</h2>
<div className='card bg-base-100 border-base-200 border shadow'>
@@ -1,12 +1,13 @@
import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import { useReaderStore } from '@/store/readerStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import cssbeautify from 'cssbeautify';
import { getStyles } from '@/utils/style';
import { useTheme } from '@/hooks/useTheme';
import cssbeautify from 'cssbeautify';
import cssValidate from '@/utils/css';
const cssRegex =
/((?:\s*)([\w#.@*,:\-.:>+~$$$$\"=(),*\s]+)\s*{(?:[\s]*)((?:[^\}]+[:][^\}]+;?)*)*\s*}(?:\s*))/gim;
const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const _ = useTranslation();
@@ -17,49 +18,42 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [animated, setAnimated] = useState(viewSettings.animated!);
const [isDisableClick, setIsDisableClick] = useState(viewSettings.disableClick!);
const [draftStylesheet, setDraftStylesheet] = useState(viewSettings.userStylesheet!);
const [draftStylesheetSaved, setDraftStylesheetSaved] = useState(true);
const [userStylesheet, setUserStylesheet] = useState(viewSettings.userStylesheet!);
const [error, setError] = useState<string | null>(null);
const handleUserStylesheetChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const cssInput = e.target.value;
setDraftStylesheet(cssInput);
setDraftStylesheetSaved(false);
let cssInput = userStylesheet;
try {
const { isValid, error } = cssValidate(cssInput);
if (cssInput && !isValid) {
throw new Error(error || 'Invalid CSS');
}
setError(null);
} catch (err: unknown) {
if (err instanceof Error) {
setError(err.message);
} else {
setError('Invalid CSS: Please check your input.');
}
console.log('CSS Error:', err);
}
const validateCSS = (css: string) => {
return cssRegex.test(css);
};
const applyStyles = () => {
const formattedCSS = cssbeautify(draftStylesheet, {
indent: ' ',
openbrace: 'end-of-line',
autosemicolon: true,
});
const handleUserStylesheetChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
cssInput = e.target.value;
setDraftStylesheet(formattedCSS);
setDraftStylesheetSaved(true);
viewSettings.userStylesheet = formattedCSS;
setViewSettings(bookKey, viewSettings);
try {
const formattedCSS = cssbeautify(cssInput, {
indent: ' ',
openbrace: 'end-of-line',
autosemicolon: true,
});
setUserStylesheet(formattedCSS);
if (isFontLayoutSettingsGlobal) {
settings.globalViewSettings.userStylesheet = formattedCSS;
setSettings(settings);
if (cssInput && !validateCSS(cssInput)) {
throw new Error('Invalid CSS');
}
setError(null);
viewSettings.userStylesheet = formattedCSS;
setViewSettings(bookKey, viewSettings);
if (isFontLayoutSettingsGlobal) {
settings.globalViewSettings.userStylesheet = formattedCSS;
setSettings(settings);
}
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
} catch (err) {
setError('Invalid CSS: Please check your input.');
console.log('CSS Error:', err);
}
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
};
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
@@ -96,7 +90,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
<div className='my-4 w-full space-y-6'>
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Animation')}</h2>
<div className='card border-base-200 bg-base-100 border shadow'>
<div className='card bg-base-100 border shadow'>
<div className='divide-y'>
<div className='config-item config-item-top config-item-bottom'>
<span className='text-gray-700'>{_('Paging Animation')}</span>
@@ -113,7 +107,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Behavior')}</h2>
<div className='card border-base-200 bg-base-100 border shadow'>
<div className='card bg-base-100 border shadow'>
<div className='divide-y'>
<div className='config-item config-item-top config-item-bottom'>
<span className='text-gray-700'>{_('Disable Click-to-Flip')}</span>
@@ -130,34 +124,22 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Custom CSS')}</h2>
<div
className={`card border-base-200 bg-base-100 border shadow ${error ? 'border-red-500' : ''}`}
>
<div className='relative p-1'>
<textarea
className='textarea textarea-ghost h-48 w-full border-0 p-3 !outline-none'
placeholder={_('Enter your custom CSS here...')}
spellCheck='false'
value={draftStylesheet}
onInput={handleInput}
onKeyDown={handleInput}
onKeyUp={handleInput}
onChange={handleUserStylesheetChange}
/>
<button
className={clsx(
'btn btn-ghost bg-base-200 absolute bottom-2 right-4 h-8 min-h-8 px-4 py-2',
draftStylesheetSaved ? 'hidden' : '',
error ? 'btn-disabled' : '',
)}
onClick={applyStyles}
disabled={!!error}
>
{_('Apply')}
</button>
<div className={`card bg-base-100 border shadow ${error ? 'border-red-500' : ''}`}>
<div className='divide-y'>
<div className='css-text-area config-item-top config-item-bottom p-1'>
<textarea
className='textarea textarea-ghost h-48 w-full border-0 p-3 !outline-none'
placeholder={_('Enter your custom CSS here...')}
spellCheck='false'
value={cssInput}
onInput={handleInput}
onKeyDown={handleInput}
onKeyUp={handleInput}
onChange={handleUserStylesheetChange}
/>
</div>
</div>
</div>
{error && <p className='mt-1 text-sm text-red-500'>{error}</p>}
</div>
</div>
);
@@ -39,7 +39,7 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
return (
<dialog className='modal modal-open min-w-90 w-full !bg-[rgba(0,0,0,0.2)]'>
<div className='modal-box settings-content flex h-[65%] w-1/2 min-w-[540px] max-w-full flex-col p-0'>
<div className='modal-box settings-content flex h-[60%] w-1/2 min-w-[480px] max-w-full flex-col p-0'>
<div className='dialog-header bg-base-100 sticky top-0 z-10 flex items-center justify-center px-4 pt-2'>
<div className='dialog-tabs flex h-10 max-w-[80%] flex-grow items-center justify-around'>
<button
@@ -1,22 +1,20 @@
import React from 'react';
import Image from 'next/image';
import { MdInfoOutline } from 'react-icons/md';
import { Book } from '@/types/book';
import { useTranslation } from '@/hooks/useTranslation';
import { eventDispatcher } from '@/utils/event';
const BookCard = ({ book }: { book: Book }) => {
const { coverImageUrl, title, author } = book;
interface BookCardProps {
cover: string;
title: string;
author: string;
}
const BookCard: React.FC<BookCardProps> = ({ cover, title, author }) => {
const _ = useTranslation();
const showBookDetails = () => {
eventDispatcher.dispatchSync('show-book-details', book);
};
return (
<div className='flex h-20 w-full items-center'>
<Image
src={coverImageUrl!}
src={cover}
alt={_('Book Cover')}
width={56}
height={80}
@@ -33,7 +31,7 @@ const BookCard = ({ book }: { book: Book }) => {
className='btn btn-ghost hover:bg-base-300 h-6 min-h-6 w-6 rounded-full p-0 transition-colors'
aria-label={_('More Info')}
>
<MdInfoOutline size={18} className='fill-base-content' onClick={showBookDetails} />
<MdInfoOutline size={18} className='fill-base-content' />
</button>
</div>
);
@@ -10,8 +10,7 @@ import { BookSearchConfig, BookSearchResult } from '@/types/book';
import Dropdown from '@/components/Dropdown';
import SearchOptions from './SearchOptions';
const MINIMUM_SEARCH_TERM_LENGTH_DEFAULT = 2;
const MINIMUM_SEARCH_TERM_LENGTH_CJK = 1;
const MINIMUM_SEARCH_TERM_LENGTH = 2;
interface SearchBarProps {
isVisible: boolean;
@@ -98,15 +97,8 @@ const SearchBar: React.FC<SearchBarProps> = ({
handleSearchTermChange(searchTerm);
};
const exceedMinSearchTermLength = (searchTerm: string) => {
const isCJK = /[\u4e00-\u9fa5\u3040-\u30ff\uac00-\ud7af]/.test(searchTerm);
const minLength = isCJK ? MINIMUM_SEARCH_TERM_LENGTH_CJK : MINIMUM_SEARCH_TERM_LENGTH_DEFAULT;
return searchTerm.length >= minLength;
};
const handleSearchTermChange = (term: string) => {
if (exceedMinSearchTermLength(term)) {
if (term.length >= MINIMUM_SEARCH_TERM_LENGTH) {
handleSearch(term);
} else {
resetSearch();
@@ -126,10 +118,7 @@ const SearchBar: React.FC<SearchBarProps> = ({
onSearchResultChange([...results]);
isSearchPending.current = false;
console.log('search done');
if (
queuedSearchTerm.current !== term &&
exceedMinSearchTermLength(queuedSearchTerm.current)
) {
if (queuedSearchTerm.current !== term && queuedSearchTerm.current.length > 2) {
handleSearch(queuedSearchTerm.current);
}
}
@@ -26,7 +26,7 @@ const SearchResultItem: React.FC<SearchResultItemProps> = ({
ref={viewRef}
className={clsx(
'my-2 cursor-pointer rounded-lg p-2 text-sm',
isCurrent ? 'bg-base-300 hover:bg-gray-300/70' : 'hover:bg-base-300 bg-base-100',
isCurrent ? 'bg-base-300 hover:bg-gray-300/70' : 'hover:bg-base-300 bg-white',
)}
onClick={() => onSelectResult(cfi)}
>
@@ -125,7 +125,7 @@ const SideBar: React.FC<{
/>
</div>
<div className='border-base-300/50 border-b px-3'>
<BookCard book={book} />
<BookCard cover={book.coverImageUrl!} title={book.title} author={book.author} />
</div>
</div>
{isSearchBarVisible && searchResults ? (
+2 -2
View File
@@ -2,7 +2,7 @@
import { useEffect } from 'react';
import { useTheme } from '@/hooks/useTheme';
import { hasUpdater } from '@/services/environment';
import { isTauriAppPlatform } from '@/services/environment';
import { checkForAppUpdates } from '@/helpers/updater';
import Reader from './components/Reader';
@@ -10,7 +10,7 @@ export default function Page() {
useTheme();
useEffect(() => {
const doAppUpdates = async () => {
if (hasUpdater()) {
if (isTauriAppPlatform()) {
await checkForAppUpdates();
}
};
@@ -9,7 +9,6 @@ import { useTranslation } from '@/hooks/useTranslation';
import { formatDate, formatSubject } from '@/utils/book';
import WindowButtons from '@/components/WindowButtons';
import Spinner from './Spinner';
import { BookDoc } from '@/libs/document';
interface BookDetailModalProps {
book: Book;
@@ -20,25 +19,19 @@ interface BookDetailModalProps {
const BookDetailModal = ({ book, isOpen, onClose }: BookDetailModalProps) => {
const _ = useTranslation();
const [loading, setLoading] = useState(false);
const [bookMeta, setBookMeta] = useState<BookDoc['metadata'] | null>(null);
const [bookMeta, setBookMeta] = useState<null | {
title: string;
language: string | string[];
editor?: string;
publisher?: string;
published?: string;
description?: string;
subject?: string[];
identifier?: string;
}>(null);
const { envConfig } = useEnv();
const { settings } = useSettingsStore();
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, onClose]);
useEffect(() => {
const loadingTimeout = setTimeout(() => setLoading(true), 300);
const fetchBookDetails = async () => {
@@ -105,10 +98,10 @@ const BookDetailModal = ({ book, isOpen, onClose }: BookDetailModalProps) => {
</div>
</div>
<div className='title-author flex h-40 max-w-[60%] flex-col justify-between pr-4'>
<div className='title-author flex h-40 flex-col justify-between pr-4'>
<div>
<h2 className='text-base-content mb-2 line-clamp-2 break-all text-2xl font-bold'>
{book.title || _('Untitled')}
<h2 className='text-base-content mb-2 line-clamp-2 text-2xl font-bold'>
{bookMeta.title || _('Untitled')}
</h2>
<p className='text-neutral-content line-clamp-1'>{book.author || _('Unknown')}</p>
</div>
+2 -2
View File
@@ -34,7 +34,7 @@ const Dropdown: React.FC<DropdownProps> = ({
: children;
return (
<div className='dropdown-container'>
<>
{isOpen && (
<div className='fixed inset-0 bg-transparent' onClick={() => setIsDropdownOpen(false)} />
)}
@@ -48,7 +48,7 @@ const Dropdown: React.FC<DropdownProps> = ({
</div>
{isOpen && childrenWithToggle}
</div>
</div>
</>
);
};
+1 -1
View File
@@ -67,7 +67,7 @@ const Popup = ({
/>
<div
id='popup-container'
className={`bg-base-200 absolute z-30 rounded-lg font-sans shadow-xl ${className}`}
className={`bg-base-200 absolute rounded-lg font-sans shadow-xl ${className}`}
style={{
width: `${width}px`,
height: `${height}px`,
+1 -1
View File
@@ -8,7 +8,7 @@ const Toast: React.FC<{ message: string; toastClass?: string; alertClass?: strin
}) => (
<div className={clsx('toast toast-center toast-middle', toastClass)}>
<div className={clsx('alert flex items-center justify-center border-0', alertClass)}>
<span className='whitespace-normal break-words'>{message}</span>
<span>{message}</span>
</div>
</div>
);
@@ -1,6 +1,5 @@
import clsx from 'clsx';
import React, { useEffect, useRef } from 'react';
import { useEnv } from '@/context/EnvContext';
import { tauriHandleMinimize, tauriHandleToggleMaximize, tauriHandleClose } from '@/utils/window';
import { isTauriAppPlatform } from '@/services/environment';
@@ -45,7 +44,6 @@ const WindowButtons: React.FC<WindowButtonsProps> = ({
onClose,
}) => {
const parentRef = useRef<HTMLDivElement>(null);
const { appService } = useEnv();
const handleMouseDown = async (e: MouseEvent) => {
const target = e.target as HTMLElement;
@@ -53,7 +51,6 @@ const WindowButtons: React.FC<WindowButtonsProps> = ({
if (
target.closest('.btn') ||
target.closest('.window-button') ||
target.closest('.dropdown-container') ||
target.closest('.exclude-title-bar-mousedown')
) {
return;
@@ -113,7 +110,7 @@ const WindowButtons: React.FC<WindowButtonsProps> = ({
className,
)}
>
{showMinimize && appService?.hasWindowBar && (
{showMinimize && (
<WindowButton onClick={handleMinimize} ariaLabel='Minimize' id='titlebar-minimize'>
<svg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'>
<path fill='currentColor' d='M20 14H4v-2h16' />
@@ -121,7 +118,7 @@ const WindowButtons: React.FC<WindowButtonsProps> = ({
</WindowButton>
)}
{showMaximize && appService?.hasWindowBar && (
{showMaximize && (
<WindowButton onClick={handleMaximize} ariaLabel='Maximize/Restore' id='titlebar-maximize'>
<svg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'>
<path fill='currentColor' d='M4 4h16v16H4zm2 4v10h12V8z' />
@@ -129,7 +126,7 @@ const WindowButtons: React.FC<WindowButtonsProps> = ({
</WindowButton>
)}
{showClose && (appService?.hasWindowBar || onClose) && (
{showClose && (
<WindowButton onClick={handleClose} ariaLabel='Close' id='titlebar-close'>
<svg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'>
<path
+6 -14
View File
@@ -29,8 +29,10 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
});
useEffect(() => {
const syncSession = (session: { access_token: string; user: User } | null) => {
console.log('Syncing session');
const fetchSession = async () => {
const {
data: { session },
} = await supabase.auth.getSession();
if (session) {
const { access_token, user } = session;
setToken(access_token);
@@ -44,20 +46,10 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
setUser(null);
}
};
const fetchSession = async () => {
const { data } = await supabase.auth.getSession();
syncSession(data.session);
};
console.log('Fetching session');
fetchSession();
const { data: subscription } = supabase.auth.onAuthStateChange((_, session) => {
syncSession(session);
});
return () => {
subscription?.subscription.unsubscribe();
};
}, []);
}, [token]);
const login = (newToken: string, newUser: User) => {
console.log('Logging in');
+2 -2
View File
@@ -1,4 +1,4 @@
import { isWebAppPlatform, hasCli } from '@/services/environment';
import { isWebAppPlatform } from '@/services/environment';
declare global {
interface Window {
@@ -36,7 +36,7 @@ export const parseOpenWithFiles = async () => {
if (isWebAppPlatform()) return [];
let files = parseWindowOpenWithFiles();
if (!files && hasCli()) {
if (!files) {
files = await parseCLIOpenWithFiles();
}
return files;
+1 -1
View File
@@ -28,7 +28,7 @@ const DEFAULT_SHORTCUTS: ShortcutConfig = {
onReloadPage: ['shift+r'],
onQuitApp: ['ctrl+q', 'cmd+q'],
onGoLeft: ['ArrowLeft', 'PageUp', 'h'],
onGoRight: ['ArrowRight', 'PageDown', 'l', ' '],
onGoRight: ['ArrowRight', 'PageDown', 'l'],
onGoNext: ['ArrowDown', 'j'],
onGoPrev: ['ArrowUp', 'k'],
onGoBack: ['shift+ArrowLeft', 'shift+h'],
+2 -8
View File
@@ -1,5 +1,4 @@
import { BookFormat } from '@/types/book';
import { Contributor, LanguageMap } from '@/utils/book';
import * as epubcfi from 'foliate-js/epubcfi.js';
// A groupBy polyfill for foliate-js
@@ -52,16 +51,11 @@ export interface SectionItem {
export interface BookDoc {
metadata: {
// NOTE: the title and author fields should be formatted
title: string | LanguageMap;
author: string | Contributor;
title: string;
author: string;
language: string | string[];
editor?: string;
publisher?: string;
published?: string;
description?: string;
subject?: string[];
identifier?: string;
};
toc?: Array<TOCItem>;
sections?: Array<SectionItem>;
@@ -35,7 +35,6 @@ export abstract class BaseAppService implements AppService {
abstract appPlatform: AppPlatform;
abstract isAppDataSandbox: boolean;
abstract hasTrafficLight: boolean;
abstract hasWindowBar: boolean;
abstract resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string };
abstract getCoverImageUrl(book: Book): string;
-157
View File
@@ -42,7 +42,6 @@ export const DEFAULT_BOOK_LAYOUT: BookLayout = {
maxInlineSize: 720,
maxBlockSize: 1440,
animated: false,
writingMode: 'auto',
vertical: false,
};
@@ -83,162 +82,6 @@ export const SANS_SERIF_FONTS = ['Roboto', 'Noto Sans', 'Open Sans', 'Helvetica'
export const MONOSPACE_FONTS = ['Fira Code', 'Lucida Console', 'Consolas', 'Courier New'];
export const WINDOWS_FONTS = [
'Arial',
'Arial Black',
'Bahnschrift',
'Calibri',
'Cambria',
'Cambria Math',
'Candara',
'Comic Sans MS',
'Consolas',
'Constantia',
'Corbel',
'Courier New',
'Ebrima',
'Franklin Gothic Medium',
'Gabriola',
'Gadugi',
'Georgia',
'HoloLens MDL2 Assets',
'Impact',
'Ink Free',
'Javanese Text',
'Leelawadee UI',
'Lucida Console',
'Lucida Sans Unicode',
'Malgun Gothic',
'Marlett',
'Microsoft Himalaya',
'Microsoft JhengHei',
'Microsoft New Tai Lue',
'Microsoft PhagsPa',
'Microsoft Sans Serif',
'Microsoft Tai Le',
'Microsoft YaHei',
'Microsoft Yi Baiti',
'MingLiU-ExtB',
'Mongolian Baiti',
'MS Gothic',
'MV Boli',
'Myanmar Text',
'Nirmala UI',
'Palatino Linotype',
'Segoe MDL2 Assets',
'Segoe Print',
'Segoe Script',
'Segoe UI',
'Segoe UI Historic',
'Segoe UI Emoji',
'Segoe UI Symbol',
'SimSun',
'Sitka',
'Sylfaen',
'Symbol',
'Tahoma',
'Times New Roman',
'Trebuchet MS',
'Verdana',
'Webdings',
'Wingdings',
'Yu Gothic',
];
export const MACOS_FONTS = [
'American Typewriter',
'Andale Mono',
'Arial',
'Arial Black',
'Arial Narrow',
'Arial Rounded MT Bold',
'Arial Unicode MS',
'Avenir',
'Avenir Next',
'Avenir Next Condensed',
'Baskerville',
'Big Caslon',
'Bodoni 72',
'Bodoni 72 Oldstyle',
'Bodoni 72 Smallcaps',
'Bradley Hand',
'Brush Script MT',
'Chalkboard',
'Chalkboard SE',
'Chalkduster',
'Charter',
'Cochin',
'Comic Sans MS',
'Copperplate',
'Courier',
'Courier New',
'Didot',
'DIN Alternate',
'DIN Condensed',
'Futura',
'Geneva',
'Georgia',
'Gill Sans',
'Helvetica',
'Helvetica Neue',
'Herculanum',
'Hoefler Text',
'Impact',
'Lucida Grande',
'Luminari',
'Marker Felt',
'Menlo',
'Microsoft Sans Serif',
'Monaco',
'Noteworthy',
'Optima',
'Palatino',
'Papyrus',
'Phosphate',
'Rockwell',
'Savoye LET',
'SignPainter',
'Skia',
'Snell Roundhand',
'Tahoma',
'Times',
'Times New Roman',
'Trattatello',
'Trebuchet MS',
'Verdana',
'Zapfino',
];
export const LINUX_FONTS = [
'Arial',
'Cantarell',
'Comic Sans MS',
'Courier New',
'DejaVu Sans',
'DejaVu Sans Mono',
'DejaVu Serif',
'Droid Sans',
'Droid Sans Mono',
'FreeMono',
'FreeSans',
'FreeSerif',
'Georgia',
'Impact',
'Liberation Mono',
'Liberation Sans',
'Liberation Serif',
'Noto Mono',
'Noto Sans',
'Noto Serif',
'Open Sans',
'Poppins',
'Symbola',
'Times New Roman',
'Ubuntu',
'Ubuntu Mono',
'Wingdings',
];
export const ONE_COLUMN_MAX_INLINE_SIZE = 9999;
export const BOOK_IDS_SEPARATOR = '+';
@@ -1,16 +1,7 @@
import { AppService } from '@/types/system';
declare global {
interface Window {
__READEST_CLI_ACCESS?: boolean;
__READEST_UPDATER_ACCESS?: boolean;
}
}
export const isTauriAppPlatform = () => process.env['NEXT_PUBLIC_APP_PLATFORM'] === 'tauri';
export const isWebAppPlatform = () => process.env['NEXT_PUBLIC_APP_PLATFORM'] === 'web';
export const hasUpdater = () => window.__READEST_UPDATER_ACCESS === true;
export const hasCli = () => window.__READEST_CLI_ACCESS === true;
export interface EnvConfigType {
getAppService: () => Promise<AppService>;
@@ -119,7 +119,6 @@ export class NativeAppService extends BaseAppService {
appPlatform = 'tauri' as AppPlatform;
isAppDataSandbox = isMobile;
hasTrafficLight = osType() === 'macos';
hasWindowBar = !(osType() === 'ios' || osType() === 'android');
override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } {
return resolvePath(fp, base);
@@ -180,7 +180,6 @@ export class WebAppService extends BaseAppService {
appPlatform = 'web' as AppPlatform;
isAppDataSandbox = false;
hasTrafficLight = false;
hasWindowBar = false;
override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } {
return resolvePath(fp, base);
+2 -2
View File
@@ -19,9 +19,9 @@ interface ViewState {
progress: BookProgress | null;
ribbonVisible: boolean;
/* View settings for the view:
generally view settings have a hierarchy of global settings < book settings < view settings
generally view settings have a hirarchy of global settings < book settings < view settings
view settings for primary view are saved to book config which is persisted to config file
omitting settings that are not changed from global settings */
ommitting settings that are not changed from global settings */
viewSettings: ViewSettings | null;
}
-8
View File
@@ -117,14 +117,6 @@ foliate-view {
z-index: 0;
}
.dropdown-content.bgcolor-base-200 {
background-color: theme('colors.base-200');
}
.dropdown-content.bgcolor-base-200::before {
border-bottom: 12px solid theme('colors.base-200');
z-index: 1;
}
.dropdown-left::before,
.dropdown-left::after {
left: 20px;
-1
View File
@@ -51,7 +51,6 @@ export interface BookLayout {
maxInlineSize: number;
maxBlockSize: number;
animated: boolean;
writingMode: string;
vertical: boolean;
}
-1
View File
@@ -23,7 +23,6 @@ export interface AppService {
fs: FileSystem;
appPlatform: AppPlatform;
hasTrafficLight: boolean;
hasWindowBar: boolean;
isAppDataSandbox: boolean;
selectFiles(name: string, extensions: string[]): Promise<string[]>;
+9 -10
View File
@@ -29,11 +29,11 @@ export const INIT_BOOK_CONFIG: BookConfig = {
updatedAt: 0,
};
export interface LanguageMap {
interface LanguageMap {
[key: string]: string;
}
export interface Contributor {
interface Contributor {
name: LanguageMap;
}
@@ -46,15 +46,15 @@ const formatLanguageMap = (x: string | LanguageMap): string => {
return x[userLang] || x[keys[0]!]!;
};
export const listFormater = (narrow = false, lang = userLang) => {
if (narrow) {
const listFormat = (lang: string) => {
if (lang === 'zh') {
return new Intl.ListFormat('en', { style: 'narrow', type: 'unit' });
} else {
return new Intl.ListFormat(lang, { style: 'long', type: 'conjunction' });
}
};
export const getBookLangCode = (lang: string | string[] | undefined) => {
const getBookLangCode = (lang: string | string[] | undefined) => {
try {
const bookLang = typeof lang === 'string' ? lang : lang?.[0];
return bookLang ? bookLang.split('-')[0]! : 'en';
@@ -66,10 +66,9 @@ export const getBookLangCode = (lang: string | string[] | undefined) => {
export const formatAuthors = (
bookLang: string | string[] | undefined,
contributors: string | Contributor | [string | Contributor],
) => {
const langCode = getBookLangCode(bookLang);
return Array.isArray(contributors)
? listFormater(langCode === 'zh', langCode).format(
) =>
Array.isArray(contributors)
? listFormat(getBookLangCode(bookLang)).format(
contributors.map((contributor) =>
typeof contributor === 'string' ? contributor : formatLanguageMap(contributor?.name),
),
@@ -77,7 +76,7 @@ export const formatAuthors = (
: typeof contributors === 'string'
? contributors
: formatLanguageMap(contributors?.name);
};
export const formatTitle = (title: string | LanguageMap) => {
return typeof title === 'string' ? title : formatLanguageMap(title);
};
-77
View File
@@ -1,77 +0,0 @@
const cssValidate = (css: string) => {
// Remove comments and normalize whitespace
css = css.replace(/\/\*[\s\S]*?\*\//g, '').trim();
// CSS property pattern (validate both property name and value)
const propertyPattern = /^[\s\n]*[-\w]+\s*:\s*[^;]+;?$/;
// Check if empty
if (!css) return { isValid: false, error: 'Empty CSS' };
// Ensure balanced curly braces
const openBraces = (css.match(/{/g) || []).length;
const closeBraces = (css.match(/}/g) || []).length;
if (openBraces !== closeBraces) {
return { isValid: false, error: 'Unbalanced curly braces' };
}
// Split into rule blocks
const blocks = css
.split('}')
.map((block) => block.trim())
.filter(Boolean);
for (const block of blocks) {
// Ensure the block has a selector and declarations
const parts = block.split('{').map((part) => part.trim());
if (parts.length !== 2) {
return { isValid: false, error: 'Invalid CSS structure' };
}
const [selector, decls] = parts;
// Ensure selector is not empty
if (!selector) {
return { isValid: false, error: 'Missing selector' };
}
// Ensure declarations are not empty
if (!decls) {
return { isValid: false, error: `Missing declarations for selector: ${selector}` };
}
// Validate declarations
const props = decls
.split(';')
.map((prop) => prop.trim())
.filter(Boolean);
if (props.length === 0) {
return { isValid: false, error: `No valid properties for selector: ${selector}` };
}
for (const prop of props) {
// Check if property is missing a name or value
if (!prop.includes(':')) {
return { isValid: false, error: `Missing property or value: ${prop}` };
}
const [name, value] = prop.split(':').map((part) => part.trim());
if (!name) {
return { isValid: false, error: `Missing property name: ${prop}` };
}
if (!value) {
return { isValid: false, error: `Missing property value: ${prop}` };
}
// Validate full property format
if (!propertyPattern.test(prop.endsWith(';') ? prop : prop + ';')) {
return { isValid: false, error: `Invalid property: ${prop}` };
}
}
}
return { isValid: true, error: null };
};
export default cssValidate;
+1 -4
View File
@@ -76,7 +76,6 @@ const getLayoutStyles = (
justify: boolean,
hyphenate: boolean,
zoomLevel: number,
writingMode: string,
bg: string,
fg: string,
primary: string,
@@ -102,6 +101,7 @@ const getLayoutStyles = (
html {
--theme-bg-color: ${bg};
--default-text-align: ${justify ? 'justify' : 'start'};
line-height: ${spacing};
hanging-punctuation: allow-end last;
orphans: 2;
widows: 2;
@@ -123,7 +123,6 @@ const getLayoutStyles = (
}
html, body {
color: ${fg};
${writingMode === 'auto' ? '' : `writing-mode: ${writingMode};`}
text-align: var(--default-text-align);
background-color: var(--theme-bg-color, transparent);
background: var(--background-set, none);
@@ -140,7 +139,6 @@ const getLayoutStyles = (
background-color: transparent !important;
}
p, li, blockquote, dd {
line-height: ${spacing} !important;
text-align: inherit;
-webkit-hyphens: ${hyphenate ? 'auto' : 'manual'};
hyphens: ${hyphenate ? 'auto' : 'manual'};
@@ -184,7 +182,6 @@ export const getStyles = (viewSettings: ViewSettings, themeCode: ThemeCode) => {
viewSettings.fullJustification!,
viewSettings.hyphenation!,
viewSettings.zoomLevel! / 100.0,
viewSettings.writingMode!,
themeCode.bg,
themeCode.fg,
themeCode.primary,
-10
View File
@@ -50,9 +50,6 @@ importers:
'@tauri-apps/plugin-cli':
specifier: ^2.2.0
version: 2.2.0
'@tauri-apps/plugin-deep-link':
specifier: ^2.2.0
version: 2.2.0
'@tauri-apps/plugin-dialog':
specifier: ^2.2.0
version: 2.2.0
@@ -790,9 +787,6 @@ packages:
'@tauri-apps/plugin-cli@2.2.0':
resolution: {integrity: sha512-rvNhMog9rHr01Xk+trBFKJ0eZICIvPkm9GX6ogB89/0hROU/lf+a/sb4vC0wtSeR7zrJuCSxwxYuvHCZheaYFA==}
'@tauri-apps/plugin-deep-link@2.2.0':
resolution: {integrity: sha512-H6mkxr2KZ3XJcKL44tiq6cOjCw9DL8OgU1xjn3j26Qsn+H/roPFiyhR7CHuB8Ar+sQFj4YVlfmJwtBajK2FETQ==}
'@tauri-apps/plugin-dialog@2.2.0':
resolution: {integrity: sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==}
@@ -3747,10 +3741,6 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.1.1
'@tauri-apps/plugin-deep-link@2.2.0':
dependencies:
'@tauri-apps/api': 2.1.1
'@tauri-apps/plugin-dialog@2.2.0':
dependencies:
'@tauri-apps/api': 2.1.1