forked from akai/readest
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 98870d9f3e | |||
| 31aeb2289c | |||
| 0f66ecbba1 | |||
| f0d3c9dd4e | |||
| 92aae340d9 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@readest/readest-app",
|
||||
"version": "0.9.58",
|
||||
"version": "0.9.59",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "dotenv -e .env.tauri -- next dev",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "مسح البحث",
|
||||
"Header & Footer": "الترويسة والتذييل",
|
||||
"Apply also in Scrolled Mode": "تطبيق أيضًا في وضع التمرير",
|
||||
"A new version of Readest is Available!": "يتوفر إصدار جديد من Readest!",
|
||||
"A new version of Readest is available!": "يتوفر إصدار جديد من Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "إصدار {{newVersion}} من ريديست متاح للتنزيل (الإصدار المثبت حاليًا {{currentVersion}}).",
|
||||
"Download and install now?": "هل ترغب في تنزيله وتثبيته الآن؟",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "جارٍ تنزيل {{downloaded}} من {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Suche löschen",
|
||||
"Header & Footer": "Kopf- & Fußzeile",
|
||||
"Apply also in Scrolled Mode": "Auch im Scroll-Modus anwenden",
|
||||
"A new version of Readest is Available!": "Eine neue Version von Readest ist verfügbar!",
|
||||
"A new version of Readest is available!": "Eine neue Version von Readest ist verfügbar!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} ist verfügbar (installierte Version {{currentVersion}}).",
|
||||
"Download and install now?": "Jetzt herunterladen und installieren?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Herunterladen {{downloaded}} von {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Καθαρισμός αναζήτησης",
|
||||
"Header & Footer": "Κεφαλίδα & Υποσέλιδο",
|
||||
"Apply also in Scrolled Mode": "Εφαρμογή και σε λειτουργία κύλισης",
|
||||
"A new version of Readest is Available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!",
|
||||
"A new version of Readest is available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Η έκδοση Readest {{newVersion}} είναι διαθέσιμη (έκδοση που είναι εγκατεστημένη {{currentVersion}}).",
|
||||
"Download and install now?": "Θέλετε να την κατεβάσετε και να την εγκαταστήσετε τώρα;",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Κατεβάζω {{downloaded}} από {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Limpiar búsqueda",
|
||||
"Header & Footer": "Encabezado y pie de página",
|
||||
"Apply also in Scrolled Mode": "Aplicar también en modo desplazamiento",
|
||||
"A new version of Readest is Available!": "¡Una nueva versión de Readest está disponible!",
|
||||
"A new version of Readest is available!": "¡Una nueva versión de Readest está disponible!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponible (versión instalada {{currentVersion}}).",
|
||||
"Download and install now?": "Descargar e instalar ahora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Descargando {{downloaded}} de {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Effacer la recherche",
|
||||
"Header & Footer": "En-tête et pied de page",
|
||||
"Apply also in Scrolled Mode": "Appliquer également en mode défilement",
|
||||
"A new version of Readest is Available!": "Avis de mise à jour",
|
||||
"A new version of Readest is available!": "Avis de mise à jour",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} est disponible (version installée {{currentVersion}}).",
|
||||
"Download and install now?": "Télécharger et installer maintenant ?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Téléchargement {{downloaded}} sur {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "खोज साफ करें",
|
||||
"Header & Footer": "हेडर और फ़ुटर",
|
||||
"Apply also in Scrolled Mode": "स्क्रॉल मोड में भी लागू करें",
|
||||
"A new version of Readest is Available!": "Readest का एक नया संस्करण उपलब्ध है!",
|
||||
"A new version of Readest is available!": "Readest का एक नया संस्करण उपलब्ध है!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} उपलब्ध है (स्थापित संस्करण {{currentVersion}})।",
|
||||
"Download and install now?": "अब डाउनलोड और इंस्टॉल करें?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "डाउनलोड कर रहा है {{downloaded}} का {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Hapus Pencarian",
|
||||
"Header & Footer": "Header & Footer",
|
||||
"Apply also in Scrolled Mode": "Terapkan juga di Mode Gulir",
|
||||
"A new version of Readest is Available!": "Versi baru Readest Tersedia!",
|
||||
"A new version of Readest is available!": "Versi baru Readest Tersedia!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} tersedia (versi terinstal {{currentVersion}}).",
|
||||
"Download and install now?": "Unduh dan instal sekarang?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Mengunduh {{downloaded}} dari {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Cancella ricerca",
|
||||
"Header & Footer": "Intestazione e piè di pagina",
|
||||
"Apply also in Scrolled Mode": "Applica anche in modalità scorrimento",
|
||||
"A new version of Readest is Available!": "È disponibile una nuova versione di Readest!",
|
||||
"A new version of Readest is available!": "È disponibile una nuova versione di Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} è disponibile (versione installata {{currentVersion}}).",
|
||||
"Download and install now?": "Scarica e installa ora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Scaricando {{downloaded}} di {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "検索をクリア",
|
||||
"Header & Footer": "ヘッダーとフッター",
|
||||
"Apply also in Scrolled Mode": "スクロールモードでも適用",
|
||||
"A new version of Readest is Available!": "新しいバージョンのReadestが利用可能です!",
|
||||
"A new version of Readest is available!": "新しいバージョンのReadestが利用可能です!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}が利用可能です(インストールされたバージョン{{currentVersion}})。",
|
||||
"Download and install now?": "今すぐダウンロードしてインストールしますか?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "ダウンロード中 {{downloaded}} / {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "검색 지우기",
|
||||
"Header & Footer": "헤더 및 푸터",
|
||||
"Apply also in Scrolled Mode": "스크롤 모드에서도 적용",
|
||||
"A new version of Readest is Available!": "Readest의 새 버전이 있습니다!",
|
||||
"A new version of Readest is available!": "Readest의 새 버전이 있습니다!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}가 사용 가능 (설치된 버전 {{currentVersion}}).",
|
||||
"Download and install now?": "지금 다운로드하고 설치하시겠습니까?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "다운로드 중 {{downloaded}} / {{contentLength}}",
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
"Description:": "Beschrijving:",
|
||||
"No description available": "Geen beschrijving beschikbaar",
|
||||
"Drop to Import Books": "Sleep om boeken te importeren",
|
||||
"A new version of Readest is Available!": "Een nieuwe versie van Readest is beschikbaar!",
|
||||
"A new version of Readest is available!": "Een nieuwe versie van Readest is beschikbaar!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} is beschikbaar (geïnstalleerde versie {{currentVersion}}).",
|
||||
"Download and install now?": "Nu downloaden en installeren?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Downloaden {{downloaded}} van {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Wyczyść wyszukiwanie",
|
||||
"Header & Footer": "Nagłówek i stopka",
|
||||
"Apply also in Scrolled Mode": "Zastosuj również w trybie przewijania",
|
||||
"A new version of Readest is Available!": "Nowa wersja Readest jest dostępna!",
|
||||
"A new version of Readest is available!": "Nowa wersja Readest jest dostępna!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} jest dostępny (zainstalowana wersja {{currentVersion}}).",
|
||||
"Download and install now?": "Pobierz i zainstaluj teraz?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Pobieranie {{downloaded}} z {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Limpar Pesquisa",
|
||||
"Header & Footer": "Cabeçalho e Rodapé",
|
||||
"Apply also in Scrolled Mode": "Aplicar também no Modo Rolado",
|
||||
"A new version of Readest is Available!": "Uma nova versão do Readest está disponível!",
|
||||
"A new version of Readest is available!": "Uma nova versão do Readest está disponível!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponível (versão instalada {{currentVersion}}).",
|
||||
"Download and install now?": "Baixar e instalar agora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Baixando {{downloaded}} de {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Очистить поиск",
|
||||
"Header & Footer": "Заголовок и нижний колонтитул",
|
||||
"Apply also in Scrolled Mode": "Применить также в прокручиваемом режиме",
|
||||
"A new version of Readest is Available!": "Доступна новая версия Readest!",
|
||||
"A new version of Readest is available!": "Доступна новая версия Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступен (установленная версия {{currentVersion}}).",
|
||||
"Download and install now?": "Скачать и установить сейчас?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Скачано {{downloaded}} из {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Aramayı Temizle",
|
||||
"Header & Footer": "Başlık ve Altbilgi",
|
||||
"Apply also in Scrolled Mode": "Kaydırılmış Modda da Uygula",
|
||||
"A new version of Readest is Available!": "Yeni bir Readest sürümü mevcut!",
|
||||
"A new version of Readest is available!": "Yeni bir Readest sürümü mevcut!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} mevcut (yüklü sürüm {{currentVersion}}).",
|
||||
"Download and install now?": "Şimdi indirip yükleyelim mi?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "İndiriliyor {{downloaded}}/{{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Очистити пошук",
|
||||
"Header & Footer": "Заголовок і нижній колонтитул",
|
||||
"Apply also in Scrolled Mode": "Застосувати також у прокрученому режимі",
|
||||
"A new version of Readest is Available!": "Доступна нова версія Readest!",
|
||||
"A new version of Readest is available!": "Доступна нова версія Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступна (встановлена версія {{currentVersion}}).",
|
||||
"Download and install now?": "Завантажити та встановити зараз?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Завантаження {{downloaded}} з {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "Xóa tìm kiếm",
|
||||
"Header & Footer": "Đầu trang & Chân trang",
|
||||
"Apply also in Scrolled Mode": "Áp dụng cũng trong chế độ cuộn",
|
||||
"A new version of Readest is Available!": "Đã có phiên bản mới của Readest!",
|
||||
"A new version of Readest is available!": "Đã có phiên bản mới của Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} đã có sẵn (phiên bản đã cài đặt {{currentVersion}}).",
|
||||
"Download and install now?": "Tải xuống và cài đặt ngay bây giờ?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Đang tải {{downloaded}} trong {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "页眉页脚",
|
||||
"Apply also in Scrolled Mode": "应用到滚动模式",
|
||||
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
|
||||
"A new version of Readest is available!": "有新版本的 Readest 可更新!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安装版本 {{currentVersion}})。",
|
||||
"Download and install now?": "立即下载并安装?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "正在下载 {{downloaded}} / {{contentLength}}",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "頁眉頁腳",
|
||||
"Apply also in Scrolled Mode": "滾動模式同樣適用",
|
||||
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
|
||||
"A new version of Readest is available!": "有新版本的 Readest 可更新!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安裝版本 {{currentVersion}})。",
|
||||
"Download and install now?": "立即下載並安裝?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "正在下載 {{downloaded}} / {{contentLength}}",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"releases": {
|
||||
"0.9.59": {
|
||||
"date": "2025-06-19",
|
||||
"notes": [
|
||||
"Layout: Image size now adjusts more accurately by converting screen units to pixels properly",
|
||||
"Layout: Improved header layout on iOS landscape mode — extra spacing has been removed",
|
||||
"Settings: Top and bottom margin settings are now more intuitive and better constrained"
|
||||
]
|
||||
},
|
||||
"0.9.58": {
|
||||
"date": "2025-06-18",
|
||||
"notes": [
|
||||
|
||||
@@ -29,7 +29,8 @@ interface BooksGridProps {
|
||||
const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
const { appService } = useEnv();
|
||||
const { getConfig, getBookData } = useBookDataStore();
|
||||
const { getProgress, getViewState, getViewSettings, hoveredBookKey } = useReaderStore();
|
||||
const { getProgress, getViewState, getViewSettings } = useReaderStore();
|
||||
const { getGridInsets, setGridInsets, hoveredBookKey } = useReaderStore();
|
||||
const { isSideBarVisible, sideBarBookKey } = useSidebarStore();
|
||||
const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useSettingsStore();
|
||||
|
||||
@@ -45,6 +46,21 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sideBarBookKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!screenInsets) return;
|
||||
bookKeys.forEach((bookKey, index) => {
|
||||
const { top, right, bottom, left } = getInsetEdges(index, bookKeys.length, aspectRatio);
|
||||
const gridInsets = {
|
||||
top: top ? screenInsets.top : 0,
|
||||
right: right ? screenInsets.right : 0,
|
||||
bottom: bottom ? screenInsets.bottom : 0,
|
||||
left: left ? screenInsets.left : 0,
|
||||
};
|
||||
setGridInsets(bookKey, gridInsets);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bookKeys, screenInsets]);
|
||||
|
||||
if (!screenInsets) return null;
|
||||
|
||||
return (
|
||||
@@ -56,26 +72,18 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
}}
|
||||
>
|
||||
{bookKeys.map((bookKey, index) => {
|
||||
const { top, right, bottom, left } = getInsetEdges(index, bookKeys.length, aspectRatio);
|
||||
const bookData = getBookData(bookKey);
|
||||
const config = getConfig(bookKey);
|
||||
const progress = getProgress(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey);
|
||||
const gridInsets = getGridInsets(bookKey);
|
||||
const { book, bookDoc } = bookData || {};
|
||||
if (!book || !config || !bookDoc || !viewSettings) return null;
|
||||
if (!book || !config || !bookDoc || !viewSettings || !gridInsets) return null;
|
||||
|
||||
const { section, pageinfo, timeinfo, sectionLabel } = progress || {};
|
||||
const isBookmarked = getViewState(bookKey)?.ribbonVisible;
|
||||
const horizontalGapPercent = viewSettings.gapPercent;
|
||||
const viewInsets = getViewInsets(viewSettings);
|
||||
// insets for the grid cell
|
||||
const gridInsets = {
|
||||
top: top ? screenInsets.top : 0,
|
||||
right: right ? screenInsets.right : 0,
|
||||
bottom: bottom ? screenInsets.bottom : 0,
|
||||
left: left ? screenInsets.left : 0,
|
||||
};
|
||||
// insets for the content inside the grid cell
|
||||
const contentInsets = {
|
||||
top: gridInsets.top + viewInsets.top,
|
||||
right: gridInsets.right + viewInsets.right,
|
||||
@@ -104,12 +112,13 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
isHoveredAnim={bookKeys.length > 2}
|
||||
onCloseBook={onCloseBook}
|
||||
onSetSettingsDialogOpen={setFontLayoutSettingsDialogOpen}
|
||||
gridInsets={gridInsets}
|
||||
/>
|
||||
<FoliateViewer
|
||||
bookKey={bookKey}
|
||||
bookDoc={bookDoc}
|
||||
config={config}
|
||||
insets={contentInsets}
|
||||
contentInsets={contentInsets}
|
||||
/>
|
||||
{viewSettings.vertical && viewSettings.scrolled && (
|
||||
<>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { BookDoc, getDirection } from '@/libs/document';
|
||||
import { BookConfig } from '@/types/book';
|
||||
import { FoliateView, wrappedFoliateView } from '@/types/view';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -38,8 +39,8 @@ const FoliateViewer: React.FC<{
|
||||
bookKey: string;
|
||||
bookDoc: BookDoc;
|
||||
config: BookConfig;
|
||||
insets: { top: number; right: number; bottom: number; left: number };
|
||||
}> = ({ bookKey, bookDoc, config, insets }) => {
|
||||
contentInsets: Insets;
|
||||
}> = ({ bookKey, bookDoc, config, contentInsets: insets }) => {
|
||||
const { getView, setView: setFoliateView, setProgress } = useReaderStore();
|
||||
const { getViewSettings, setViewSettings } = useReaderStore();
|
||||
const { getParallels } = useParallelViewStore();
|
||||
@@ -83,7 +84,7 @@ const FoliateViewer: React.FC<{
|
||||
.then((data) => {
|
||||
const viewSettings = getViewSettings(bookKey);
|
||||
if (viewSettings && detail.type === 'text/css')
|
||||
return transformStylesheet(viewSettings, width, height, data);
|
||||
return transformStylesheet(width, height, data);
|
||||
if (viewSettings && detail.type === 'application/xhtml+xml') {
|
||||
const ctx = {
|
||||
bookKey,
|
||||
@@ -206,9 +207,10 @@ const FoliateViewer: React.FC<{
|
||||
detail.allowScript = viewSettings.allowScript ?? false;
|
||||
}
|
||||
});
|
||||
const containerRect = containerRef.current?.getBoundingClientRect();
|
||||
const width = containerRect?.width || window.innerWidth;
|
||||
const height = containerRect?.height || window.innerHeight;
|
||||
const viewWidth = appService?.isMobile ? screen.width : window.innerWidth;
|
||||
const viewHeight = appService?.isMobile ? screen.height : window.innerHeight;
|
||||
const width = viewWidth - insets.left - insets.right;
|
||||
const height = viewHeight - insets.top - insets.bottom;
|
||||
book.transformTarget?.addEventListener('data', getDocTransformHandler({ width, height }));
|
||||
view.renderer.setStyles?.(getStyles(viewSettings));
|
||||
|
||||
@@ -247,11 +249,16 @@ const FoliateViewer: React.FC<{
|
||||
const showDoubleBorder = viewSettings.vertical && viewSettings.doubleBorder;
|
||||
const showDoubleBorderHeader = showDoubleBorder && viewSettings.showHeader;
|
||||
const showDoubleBorderFooter = showDoubleBorder && viewSettings.showFooter;
|
||||
const showTopHeader = viewSettings.showHeader && !viewSettings.vertical;
|
||||
const showBottomFooter = viewSettings.showFooter && !viewSettings.vertical;
|
||||
|
||||
const moreTopInset = showTopHeader ? Math.max(0, 44 - insets.top) : 0;
|
||||
const moreBottomInset = showBottomFooter ? Math.max(0, 44 - insets.bottom) : 0;
|
||||
const moreRightInset = showDoubleBorderHeader ? 32 : 0;
|
||||
const moreLeftInset = showDoubleBorderFooter ? 32 : 0;
|
||||
viewRef.current?.renderer.setAttribute('margin-top', `${insets.top}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-top', `${insets.top + moreTopInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-right', `${insets.right + moreRightInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-bottom', `${insets.bottom}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-bottom', `${insets.bottom + moreBottomInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-left', `${insets.left + moreLeftInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('gap', `${viewSettings.gapPercent}%`);
|
||||
if (viewSettings.scrolled) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import clsx from 'clsx';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { PiDotsThreeVerticalBold } from 'react-icons/pi';
|
||||
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -22,6 +23,7 @@ interface HeaderBarProps {
|
||||
bookTitle: string;
|
||||
isTopLeft: boolean;
|
||||
isHoveredAnim: boolean;
|
||||
gridInsets: Insets;
|
||||
onCloseBook: (bookKey: string) => void;
|
||||
onSetSettingsDialogOpen: (open: boolean) => void;
|
||||
}
|
||||
@@ -31,6 +33,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
bookTitle,
|
||||
isTopLeft,
|
||||
isHoveredAnim,
|
||||
gridInsets,
|
||||
onCloseBook,
|
||||
onSetSettingsDialogOpen,
|
||||
}) => {
|
||||
@@ -77,10 +80,10 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-base-100 absolute top-0 w-full',
|
||||
appService?.hasSafeAreaInset && 'pt-[env(safe-area-inset-top)]',
|
||||
)}
|
||||
className={clsx('bg-base-100 absolute top-0 w-full')}
|
||||
style={{
|
||||
paddingTop: appService?.hasSafeAreaInset ? `${gridInsets.top}px` : '0px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={clsx('absolute top-0 z-10 hidden h-11 w-full sm:flex')}
|
||||
@@ -89,11 +92,11 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-base-100 absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
'bg-base-100 absolute left-0 right-0 top-0 z-10',
|
||||
isVisible ? 'visible' : 'hidden',
|
||||
)}
|
||||
style={{
|
||||
height: systemUIVisible ? `max(env(safe-area-inset-top), ${statusBarHeight}px)` : '',
|
||||
height: systemUIVisible ? `${Math.max(gridInsets.top, statusBarHeight)}px` : '0px',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -110,8 +113,8 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
)}
|
||||
style={{
|
||||
marginTop: systemUIVisible
|
||||
? `max(env(safe-area-inset-top), ${statusBarHeight}px)`
|
||||
: 'env(safe-area-inset-top)',
|
||||
? `${Math.max(gridInsets.top, statusBarHeight)}px`
|
||||
: `${gridInsets.top}px`,
|
||||
}}
|
||||
onMouseLeave={() => !appService?.isMobile && setHoveredBookKey('')}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
|
||||
interface SectionInfoProps {
|
||||
@@ -22,6 +24,13 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const { appService } = useEnv();
|
||||
const { systemUIVisible, statusBarHeight } = useThemeStore();
|
||||
const topInset = Math.max(
|
||||
gridInsets.top,
|
||||
appService?.isAndroidApp && systemUIVisible ? statusBarHeight / 2 : 0,
|
||||
);
|
||||
|
||||
const [hintMessage, setHintMessage] = React.useState<string | null>(null);
|
||||
const hintTimeout = useRef(2000);
|
||||
const dismissTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
@@ -57,7 +66,7 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
height: `${topInset}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -83,7 +92,7 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
width: showDoubleBorder ? '30px' : `${horizontalGap}%`,
|
||||
}
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
top: `${topInset}px`,
|
||||
insetInlineEnd: `calc(${horizontalGap / 2}% + ${contentInsets.right}px)`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
|
||||
const { settings, setSettings } = useSettingsStore();
|
||||
const { isSideBarVisible, setSideBarVisible } = useSidebarStore();
|
||||
const { isNotebookVisible, setNotebookVisible } = useNotebookStore();
|
||||
const { isDarkMode, showSystemUI, dismissSystemUI } = useThemeStore();
|
||||
const { isDarkMode, systemUIAlwaysHidden, showSystemUI, dismissSystemUI } = useThemeStore();
|
||||
const { acquireBackKeyInterception, releaseBackKeyInterception } = useDeviceControlStore();
|
||||
const [libraryLoaded, setLibraryLoaded] = useState(false);
|
||||
const isInitiating = useRef(false);
|
||||
@@ -96,8 +96,9 @@ const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
|
||||
useEffect(() => {
|
||||
if (!appService?.isMobileApp) return;
|
||||
const systemUIVisible = !!hoveredBookKey || settings.alwaysShowStatusBar;
|
||||
setSystemUIVisibility({ visible: systemUIVisible, darkMode: isDarkMode });
|
||||
if (systemUIVisible) {
|
||||
const visible = systemUIVisible && !systemUIAlwaysHidden;
|
||||
setSystemUIVisibility({ visible, darkMode: isDarkMode });
|
||||
if (visible) {
|
||||
showSystemUI();
|
||||
} else {
|
||||
dismissSystemUI();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
|
||||
interface SectionInfoProps {
|
||||
section?: string;
|
||||
@@ -21,6 +23,13 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const { appService } = useEnv();
|
||||
const { systemUIVisible, statusBarHeight } = useThemeStore();
|
||||
const topInset = Math.max(
|
||||
gridInsets.top,
|
||||
appService?.isAndroidApp && systemUIVisible ? statusBarHeight / 2 : 0,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -29,7 +38,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
height: `${topInset}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -49,7 +58,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
}
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
top: `${topInset}px`,
|
||||
insetInlineStart: `calc(${horizontalGap / 2}% + ${contentInsets.left}px)`,
|
||||
width: `calc(100% - ${contentInsets.left + contentInsets.right}px)`,
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ import NumberInput from './NumberInput';
|
||||
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const _ = useTranslation();
|
||||
const { envConfig } = useEnv();
|
||||
const { getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const { getView, getViewSettings, getGridInsets, setViewSettings } = useReaderStore();
|
||||
const { getBookData } = useBookDataStore();
|
||||
const view = getView(bookKey);
|
||||
const bookData = getBookData(bookKey)!;
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const gridInsets = getGridInsets(bookKey)!;
|
||||
|
||||
const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin!);
|
||||
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!);
|
||||
@@ -98,42 +99,54 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (marginTopPx === viewSettings.marginTopPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
if (viewSettings.marginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'marginTopPx', marginTopPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [marginTopPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (marginBottomPx === viewSettings.marginBottomPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
if (viewSettings.marginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'marginBottomPx', marginBottomPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [marginBottomPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (marginRightPx === viewSettings.marginRightPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
if (viewSettings.marginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'marginRightPx', marginRightPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [marginRightPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (marginLeftPx === viewSettings.marginLeftPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
if (viewSettings.marginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'marginLeftPx', marginLeftPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [marginLeftPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginTopPx === viewSettings.compactMarginTopPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
if (viewSettings.compactMarginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginTopPx', compactMarginTopPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginTopPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginBottomPx === viewSettings.compactMarginBottomPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
if (viewSettings.compactMarginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(
|
||||
envConfig,
|
||||
bookKey,
|
||||
@@ -147,7 +160,9 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginRightPx === viewSettings.compactMarginRightPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
if (viewSettings.compactMarginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(
|
||||
envConfig,
|
||||
bookKey,
|
||||
@@ -161,7 +176,9 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginLeftPx === viewSettings.compactMarginLeftPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
if (viewSettings.compactMarginPx !== undefined) {
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginPx', undefined, false, false);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginLeftPx', compactMarginLeftPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginLeftPx]);
|
||||
@@ -257,7 +274,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showHeader === viewSettings.showHeader) return;
|
||||
if (showHeader && !viewSettings.vertical) {
|
||||
viewSettings.marginTopPx = Math.max(viewSettings.marginTopPx, 44);
|
||||
const minMarginTop = Math.max(0, Math.round((44 - gridInsets.top) / 4) * 4);
|
||||
viewSettings.marginTopPx = Math.max(viewSettings.marginTopPx, minMarginTop);
|
||||
setMarginTopPx(viewSettings.marginTopPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
@@ -269,7 +287,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showFooter === viewSettings.showFooter) return;
|
||||
if (showFooter && !viewSettings.vertical) {
|
||||
viewSettings.marginBottomPx = Math.max(viewSettings.marginBottomPx, 44);
|
||||
const minMarginBottom = Math.max(0, Math.round((44 - gridInsets.bottom) / 4) * 4);
|
||||
viewSettings.marginBottomPx = Math.max(viewSettings.marginBottomPx, minMarginBottom);
|
||||
setMarginBottomPx(viewSettings.marginBottomPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
@@ -446,7 +465,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
label={_('Top Margin (px)')}
|
||||
value={showHeader && !isVertical ? marginTopPx : compactMarginTopPx}
|
||||
onChange={showHeader && !isVertical ? setMarginTopPx : setCompactMarginTopPx}
|
||||
min={0}
|
||||
min={
|
||||
showHeader && !isVertical
|
||||
? Math.max(0, Math.round((44 - gridInsets.top) / 4) * 4)
|
||||
: 0
|
||||
}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
@@ -454,7 +477,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
label={_('Bottom Margin (px)')}
|
||||
value={showFooter && !isVertical ? marginBottomPx : compactMarginBottomPx}
|
||||
onChange={showFooter && !isVertical ? setMarginBottomPx : setCompactMarginBottomPx}
|
||||
min={0}
|
||||
min={
|
||||
showFooter && !isVertical
|
||||
? Math.max(0, Math.round((44 - gridInsets.bottom) / 4) * 4)
|
||||
: 0
|
||||
}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
|
||||
@@ -133,7 +133,9 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item }) => {
|
||||
>
|
||||
<div className='flex cursor-default items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<span className='text-gray-500'>{dayjs(item.createdAt).fromNow()}</span>
|
||||
<span className='text-sm text-gray-500 sm:text-xs'>
|
||||
{dayjs(item.createdAt).fromNow()}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center justify-end space-x-3 p-2' dir='ltr'>
|
||||
{item.note && (
|
||||
|
||||
@@ -192,7 +192,7 @@ const Dialog: React.FC<DialogProps> = ({
|
||||
>
|
||||
<div className='bg-base-content/50 h-1 w-10 rounded-full'></div>
|
||||
</div>
|
||||
<div className='dialog-header bg-base-100 sticky top-1 z-10 flex items-center justify-between px-4'>
|
||||
<div className='dialog-header bg-base-100 sticky top-1 z-10 flex items-center justify-between px-2 sm:px-4'>
|
||||
{header ? (
|
||||
header
|
||||
) : (
|
||||
|
||||
@@ -274,7 +274,7 @@ export const UpdaterContent = ({ version }: { version?: string }) => {
|
||||
|
||||
<div className='text-base-content flex-grow text-sm'>
|
||||
<h2 className='mb-4 text-center font-bold sm:text-start'>
|
||||
{_('A new version of Readest is Available!')}
|
||||
{_('A new version of Readest is available!')}
|
||||
</h2>
|
||||
<p className='mb-2'>
|
||||
{_('Readest {{newVersion}} is available (installed version {{currentVersion}}).', {
|
||||
|
||||
@@ -24,6 +24,8 @@ export const useTheme = ({
|
||||
dismissSystemUI,
|
||||
updateAppTheme,
|
||||
setStatusBarHeight,
|
||||
systemUIAlwaysHidden,
|
||||
setSystemUIAlwaysHidden,
|
||||
} = useThemeStore();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -41,12 +43,7 @@ export const useTheme = ({
|
||||
const handleSystemUIVisibility = useCallback(() => {
|
||||
if (!appService?.isMobileApp) return;
|
||||
|
||||
// This is a workaround for iPhone apps where the system UI is not visible in landscape mode
|
||||
// when the app is in fullscreen mode until we find a better solution to override the prefersStatusBarHidden
|
||||
// in the ViewController.
|
||||
const isIPhoneApp = appService.isIOSApp && getOSPlatform() === 'ios';
|
||||
const systemUINeverVisible = isIPhoneApp && screen.orientation.type.includes('landscape');
|
||||
const visible = systemUIVisible && !systemUINeverVisible;
|
||||
const visible = systemUIVisible && !systemUIAlwaysHidden;
|
||||
if (visible) {
|
||||
showSystemUI();
|
||||
} else {
|
||||
@@ -73,8 +70,12 @@ export const useTheme = ({
|
||||
}
|
||||
};
|
||||
const handleOrientationChange = () => {
|
||||
// Only handle orientation change for iPhone apps
|
||||
if (appService?.isIOSApp && getOSPlatform() === 'ios') {
|
||||
// FIXME: This is a workaround for iPhone apps where the system UI is not visible in landscape mode
|
||||
// when the app is in fullscreen mode until we find a better solution to override the prefersStatusBarHidden
|
||||
// in the ViewController. Note that screen.orientation.type is not abailable in iOS before 16.4.
|
||||
const systemUIAlwaysHidden = screen.orientation?.type.includes('landscape');
|
||||
setSystemUIAlwaysHidden(systemUIAlwaysHidden);
|
||||
handleSystemUIVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ export const DEFAULT_BOOK_FONT: BookFont = {
|
||||
|
||||
export const DEFAULT_BOOK_LAYOUT: BookLayout = {
|
||||
marginTopPx: 44,
|
||||
marginBottomPx: 16,
|
||||
marginBottomPx: 44,
|
||||
marginLeftPx: 16,
|
||||
marginRightPx: 16,
|
||||
compactMarginTopPx: 16,
|
||||
@@ -136,6 +136,7 @@ export const DEFAULT_MOBILE_VIEW_SETTINGS: Partial<ViewSettings> = {
|
||||
fullJustification: false,
|
||||
animated: true,
|
||||
defaultFont: 'Sans-serif',
|
||||
marginBottomPx: 16,
|
||||
};
|
||||
|
||||
export const DEFAULT_CJK_VIEW_SETTINGS: Partial<ViewSettings> = {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ViewSettings,
|
||||
TimeInfo,
|
||||
} from '@/types/book';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { EnvConfigType } from '@/services/environment';
|
||||
import { FoliateView } from '@/types/view';
|
||||
import { DocumentLoader, TOCItem } from '@/libs/document';
|
||||
@@ -27,6 +28,7 @@ interface ViewState {
|
||||
progress: BookProgress | null;
|
||||
ribbonVisible: boolean;
|
||||
ttsEnabled: boolean;
|
||||
gridInsets: Insets | null;
|
||||
/* View settings for the view:
|
||||
generally view settings have a hierarchy of global settings < book settings < view settings
|
||||
view settings for primary view are saved to book config which is persisted to config file
|
||||
@@ -67,6 +69,8 @@ interface ReaderStore {
|
||||
) => Promise<void>;
|
||||
clearViewState: (key: string) => void;
|
||||
getViewState: (key: string) => ViewState | null;
|
||||
getGridInsets: (key: string) => Insets | null;
|
||||
setGridInsets: (key: string, insets: Insets | null) => void;
|
||||
}
|
||||
|
||||
export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
@@ -114,6 +118,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: null,
|
||||
},
|
||||
},
|
||||
@@ -159,6 +164,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: { ...globalViewSettings, ...configViewSettings },
|
||||
},
|
||||
},
|
||||
@@ -178,6 +184,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: null,
|
||||
},
|
||||
},
|
||||
@@ -306,4 +313,16 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
getGridInsets: (key: string) => get().viewStates[key]?.gridInsets || null,
|
||||
setGridInsets: (key: string, insets: Insets | null) =>
|
||||
set((state) => ({
|
||||
viewStates: {
|
||||
...state.viewStates,
|
||||
[key]: {
|
||||
...state.viewStates[key]!,
|
||||
gridInsets: insets,
|
||||
},
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
@@ -12,6 +12,8 @@ interface ThemeState {
|
||||
isDarkMode: boolean;
|
||||
systemUIVisible: boolean;
|
||||
statusBarHeight: number;
|
||||
systemUIAlwaysHidden: boolean;
|
||||
setSystemUIAlwaysHidden: (hidden: boolean) => void;
|
||||
setStatusBarHeight: (height: number) => void;
|
||||
showSystemUI: () => void;
|
||||
dismissSystemUI: () => void;
|
||||
@@ -69,9 +71,11 @@ export const useThemeStore = create<ThemeState>((set, get) => {
|
||||
themeCode,
|
||||
systemUIVisible: false,
|
||||
statusBarHeight: 24,
|
||||
systemUIAlwaysHidden: false,
|
||||
showSystemUI: () => set({ systemUIVisible: true }),
|
||||
dismissSystemUI: () => set({ systemUIVisible: false }),
|
||||
setStatusBarHeight: (height: number) => set({ statusBarHeight: height }),
|
||||
setSystemUIAlwaysHidden: (hidden: boolean) => set({ systemUIAlwaysHidden: hidden }),
|
||||
getIsDarkMode: () => get().isDarkMode,
|
||||
setThemeMode: (mode) => {
|
||||
if (typeof window !== 'undefined' && localStorage) {
|
||||
|
||||
@@ -420,16 +420,9 @@ export const getStyles = (viewSettings: ViewSettings, themeCode?: ThemeCode) =>
|
||||
return `${layoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${userStylesheet}`;
|
||||
};
|
||||
|
||||
export const transformStylesheet = (
|
||||
viewSettings: ViewSettings,
|
||||
width: number,
|
||||
height: number,
|
||||
css: string,
|
||||
) => {
|
||||
export const transformStylesheet = (vw: number, vh: number, css: string) => {
|
||||
const isMobile = ['ios', 'android'].includes(getOSPlatform());
|
||||
const fontScale = isMobile ? 1.25 : 1;
|
||||
const w = width - viewSettings.marginLeftPx - viewSettings.marginRightPx;
|
||||
const h = height - viewSettings.marginTopPx - viewSettings.marginBottomPx;
|
||||
const ruleRegex = /([^{]+)({[^}]+})/g;
|
||||
css = css.replace(ruleRegex, (match, selector, block) => {
|
||||
const hasTextAlignCenter = /text-align\s*:\s*center\s*[;$]/.test(block);
|
||||
@@ -467,8 +460,8 @@ export const transformStylesheet = (
|
||||
const rem = parseFloat(pt) / fontScale / 12;
|
||||
return `font-size: ${rem}rem`;
|
||||
})
|
||||
.replace(/(\d*\.?\d+)vw/gi, (_, d) => (parseFloat(d) * w) / 100 + 'px')
|
||||
.replace(/(\d*\.?\d+)vh/gi, (_, d) => (parseFloat(d) * h) / 100 + 'px')
|
||||
.replace(/(\d*\.?\d+)vw/gi, (_, d) => (parseFloat(d) * vw) / 100 + 'px')
|
||||
.replace(/(\d*\.?\d+)vh/gi, (_, d) => (parseFloat(d) * vh) / 100 + 'px')
|
||||
.replace(/[\s;]color\s*:\s*black/gi, 'color: var(--theme-fg-color)')
|
||||
.replace(/[\s;]color\s*:\s*#000000/gi, 'color: var(--theme-fg-color)')
|
||||
.replace(/[\s;]color\s*:\s*#000/gi, 'color: var(--theme-fg-color)')
|
||||
|
||||
Reference in New Issue
Block a user