settings: individually adjust top, bottom, left, and right margins on the reader page (#1410)
This commit is contained in:
@@ -114,8 +114,6 @@
|
||||
"Your Library": "المكتبة الخاصة بك",
|
||||
"TTS not supported for PDF": "القراءة الصوتية غير مدعومة لملفات PDF",
|
||||
"Override Book Font": "تجاوز خط الكتاب",
|
||||
"Vertical Margins (px)": "هوامش عمودية (بكسل)",
|
||||
"Horizontal Margins (%)": "هوامش أفقية (%)",
|
||||
"Apply to All Books": "تطبيق على جميع الكتب",
|
||||
"Apply to This Book": "تطبيق على هذا الكتاب",
|
||||
"Unable to fetch the translation. Try again later.": "تعذر جلب الترجمة. حاول مرة أخرى لاحقًا.",
|
||||
@@ -371,5 +369,10 @@
|
||||
"Contact Support": "اتصل بالدعم الفني",
|
||||
"Code Highlighting": "تمييز الكود",
|
||||
"Enable Highlighting": "تمكين التمييز",
|
||||
"Code Language": "لغة الكود"
|
||||
"Code Language": "لغة الكود",
|
||||
"Top Margin (px)": "الهامش العلوي (بكسل)",
|
||||
"Bottom Margin (px)": "الهامش السفلي (بكسل)",
|
||||
"Right Margin (px)": "الهامش الأيمن (بكسل)",
|
||||
"Left Margin (px)": "الهامش الأيسر (بكسل)",
|
||||
"Column Gap (%)": "تباعد الأعمدة (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Ihre Bibliothek",
|
||||
"TTS not supported for PDF": "TTS wird für PDF nicht unterstützt",
|
||||
"Override Book Font": "Buch-Schriftart überschreiben",
|
||||
"Vertical Margins (px)": "Vertikale Ränder (px)",
|
||||
"Horizontal Margins (%)": "Horizontale Ränder (%)",
|
||||
"Apply to All Books": "Auf alle Bücher anwenden",
|
||||
"Apply to This Book": "Auf dieses Buch anwenden",
|
||||
"Unable to fetch the translation. Try again later.": "Übersetzung konnte nicht abgerufen werden. Versuchen Sie es später erneut.",
|
||||
@@ -359,5 +357,10 @@
|
||||
"Contact Support": "Support kontaktieren",
|
||||
"Code Highlighting": "Code-Hervorhebung",
|
||||
"Enable Highlighting": "Code-Hervorhebung aktivieren",
|
||||
"Code Language": "Programmiersprache"
|
||||
"Code Language": "Programmiersprache",
|
||||
"Top Margin (px)": "Oberer Rand (px)",
|
||||
"Bottom Margin (px)": "Unterer Rand (px)",
|
||||
"Right Margin (px)": "Rechter Rand (px)",
|
||||
"Left Margin (px)": "Linker Rand (px)",
|
||||
"Column Gap (%)": "Spaltenabstand (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Η βιβλιοθήκη σας",
|
||||
"TTS not supported for PDF": "Η μετατροπή κειμένου σε ομιλία δεν υποστηρίζεται για αρχεία PDF",
|
||||
"Override Book Font": "Παράκαμψη γραμματοσειράς βιβλίου",
|
||||
"Vertical Margins (px)": "Κάθετα περιθώρια (px)",
|
||||
"Horizontal Margins (%)": "Οριζόντια περιθώρια (%)",
|
||||
"Apply to All Books": "Εφαρμογή σε όλα τα βιβλία",
|
||||
"Apply to This Book": "Εφαρμογή σε αυτό το βιβλίο",
|
||||
"Unable to fetch the translation. Try again later.": "Αδυναμία λήψης της μετάφρασης. Δοκιμάστε ξανά αργότερα.",
|
||||
@@ -359,5 +357,10 @@
|
||||
"Contact Support": "Επικοινωνήστε με την υποστήριξη",
|
||||
"Code Highlighting": "Επισήμανση κώδικα",
|
||||
"Enable Highlighting": "Ενεργοποίηση επισήμανσης",
|
||||
"Code Language": "Γλώσσα κώδικα"
|
||||
"Code Language": "Γλώσσα κώδικα",
|
||||
"Top Margin (px)": "Πάνω περιθώριο (px)",
|
||||
"Bottom Margin (px)": "Κάτω περιθώριο (px)",
|
||||
"Right Margin (px)": "Δεξί περιθώριο (px)",
|
||||
"Left Margin (px)": "Αριστερό περιθώριο (px)",
|
||||
"Column Gap (%)": "Απόσταση στηλών (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Tu biblioteca",
|
||||
"TTS not supported for PDF": "TTS no es compatible con PDF",
|
||||
"Override Book Font": "Sobrescribir fuente del libro",
|
||||
"Vertical Margins (px)": "Márgenes verticales (px)",
|
||||
"Horizontal Margins (%)": "Márgenes horizontales (%)",
|
||||
"Apply to All Books": "Aplicar a todos los libros",
|
||||
"Apply to This Book": "Aplicar a este libro",
|
||||
"Unable to fetch the translation. Try again later.": "No se puede obtener la traducción. Inténtalo de nuevo más tarde.",
|
||||
@@ -362,5 +360,10 @@
|
||||
"Contact Support": "Contactar con soporte",
|
||||
"Code Highlighting": "Resaltado de código",
|
||||
"Enable Highlighting": "Activar resaltado",
|
||||
"Code Language": "Lenguaje de código"
|
||||
"Code Language": "Lenguaje de código",
|
||||
"Top Margin (px)": "Margen superior (px)",
|
||||
"Bottom Margin (px)": "Margen inferior (px)",
|
||||
"Right Margin (px)": "Margen derecho (px)",
|
||||
"Left Margin (px)": "Margen izquierdo (px)",
|
||||
"Column Gap (%)": "Espacio entre columnas (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Votre bibliothèque",
|
||||
"TTS not supported for PDF": "La synthèse vocale n'est pas prise en charge pour les fichiers PDF",
|
||||
"Override Book Font": "Remplacer la police du livre",
|
||||
"Vertical Margins (px)": "Marges verticales (px)",
|
||||
"Horizontal Margins (%)": "Marges horizontales (%)",
|
||||
"Apply to All Books": "Appliquer à tous les livres",
|
||||
"Apply to This Book": "Appliquer à ce livre",
|
||||
"Unable to fetch the translation. Try again later.": "Impossible de récupérer la traduction. Réessayez plus tard.",
|
||||
@@ -362,5 +360,10 @@
|
||||
"Contact Support": "Contacter le support",
|
||||
"Code Highlighting": "Coloration syntaxique",
|
||||
"Enable Highlighting": "Activer la coloration syntaxique",
|
||||
"Code Language": "Langage de programmation"
|
||||
"Code Language": "Langage de programmation",
|
||||
"Top Margin (px)": "Marge supérieure (px)",
|
||||
"Bottom Margin (px)": "Marge inférieure (px)",
|
||||
"Right Margin (px)": "Marge droite (px)",
|
||||
"Left Margin (px)": "Marge gauche (px)",
|
||||
"Column Gap (%)": "Espacement des colonnes (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "आपकी लाइब्रेरी",
|
||||
"TTS not supported for PDF": "PDF के लिए TTS समर्थित नहीं है",
|
||||
"Override Book Font": "पुस्तक फ़ॉन्ट ओवरराइड करें",
|
||||
"Vertical Margins (px)": "ऊर्ध्वाधर मार्जिन (px)",
|
||||
"Horizontal Margins (%)": "क्षैतिज मार्जिन (%)",
|
||||
"Apply to All Books": "सभी पुस्तकों पर लागू करें",
|
||||
"Apply to This Book": "इस पुस्तक पर लागू करें",
|
||||
"Unable to fetch the translation. Try again later.": "अनुवाद प्राप्त करने में असमर्थ। बाद में पुनः प्रयास करें।",
|
||||
@@ -359,5 +357,10 @@
|
||||
"Contact Support": "सहायता से संपर्क करें",
|
||||
"Code Highlighting": "कोड हाइलाइटिंग",
|
||||
"Enable Highlighting": "हाइलाइटिंग सक्षम करें",
|
||||
"Code Language": "कोड भाषा"
|
||||
"Code Language": "कोड भाषा",
|
||||
"Top Margin (px)": "ऊपरी हाशिया (px)",
|
||||
"Bottom Margin (px)": "निचला हाशिया (px)",
|
||||
"Right Margin (px)": "दायां हाशिया (px)",
|
||||
"Left Margin (px)": "बायां हाशिया (px)",
|
||||
"Column Gap (%)": "स्तंभों के बीच की दूरी (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Perpustakaan Anda",
|
||||
"TTS not supported for PDF": "TTS tidak didukung untuk PDF",
|
||||
"Override Book Font": "Ganti Font Buku",
|
||||
"Vertical Margins (px)": "Margin Vertikal (px)",
|
||||
"Horizontal Margins (%)": "Margin Horizontal (%)",
|
||||
"Apply to All Books": "Terapkan ke semua buku",
|
||||
"Apply to This Book": "Terapkan ke buku ini",
|
||||
"Unable to fetch the translation. Try again later.": "Tidak dapat mengambil terjemahan. Coba lagi nanti.",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "Hubungi Dukungan",
|
||||
"Code Highlighting": "Penyorotan Kode",
|
||||
"Enable Highlighting": "Aktifkan Penyorotan",
|
||||
"Code Language": "Bahasa Kode"
|
||||
"Code Language": "Bahasa Kode",
|
||||
"Top Margin (px)": "Margin Atas (px)",
|
||||
"Bottom Margin (px)": "Margin Bawah (px)",
|
||||
"Right Margin (px)": "Margin Kanan (px)",
|
||||
"Left Margin (px)": "Margin Kiri (px)",
|
||||
"Column Gap (%)": "Jarak Antar Kolom (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "La tua biblioteca",
|
||||
"TTS not supported for PDF": "TTS non supportato per PDF",
|
||||
"Override Book Font": "Sovrascrivi font libro",
|
||||
"Vertical Margins (px)": "Margini verticali (px)",
|
||||
"Horizontal Margins (%)": "Margini orizzontali (%)",
|
||||
"Apply to All Books": "Applica a tutti i libri",
|
||||
"Apply to This Book": "Applica a questo libro",
|
||||
"Unable to fetch the translation. Try again later.": "Impossibile recuperare la traduzione. Riprova più tardi.",
|
||||
@@ -362,5 +360,10 @@
|
||||
"Contact Support": "Contatta il supporto",
|
||||
"Code Highlighting": "Evidenziazione codice",
|
||||
"Enable Highlighting": "Abilita evidenziazione",
|
||||
"Code Language": "Tipo di codice"
|
||||
"Code Language": "Tipo di codice",
|
||||
"Top Margin (px)": "Margine Superiore (px)",
|
||||
"Bottom Margin (px)": "Margine Inferiore (px)",
|
||||
"Right Margin (px)": "Margine Destro (px)",
|
||||
"Left Margin (px)": "Margine Sinistro (px)",
|
||||
"Column Gap (%)": "Spazio tra Colonne (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "ライブラリー",
|
||||
"TTS not supported for PDF": "PDFではTTSがサポートされていません",
|
||||
"Override Book Font": "書籍フォントを上書き",
|
||||
"Vertical Margins (px)": "縦マージン(px)",
|
||||
"Horizontal Margins (%)": "横マージン(%)",
|
||||
"Apply to All Books": "すべての書籍に適用",
|
||||
"Apply to This Book": "この書籍に適用",
|
||||
"Unable to fetch the translation. Try again later.": "翻訳を取得できません。後で再試行してください。",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "サポートに連絡",
|
||||
"Code Highlighting": "コードハイライト",
|
||||
"Enable Highlighting": "ハイライトを有効にする",
|
||||
"Code Language": "コード言語"
|
||||
"Code Language": "コード言語",
|
||||
"Top Margin (px)": "上マージン (px)",
|
||||
"Bottom Margin (px)": "下マージン (px)",
|
||||
"Right Margin (px)": "右マージン (px)",
|
||||
"Left Margin (px)": "左マージン (px)",
|
||||
"Column Gap (%)": "列間の間隔 (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "내 서재",
|
||||
"TTS not supported for PDF": "PDF에서 TTS가 지원되지 않음",
|
||||
"Override Book Font": "책 글꼴 덮어쓰기",
|
||||
"Vertical Margins (px)": "수직 여백 (px)",
|
||||
"Horizontal Margins (%)": "수평 여백 (%)",
|
||||
"Apply to All Books": "모든 책에 적용",
|
||||
"Apply to This Book": "이 책에 적용",
|
||||
"Unable to fetch the translation. Try again later.": "번역을 가져올 수 없습니다. 나중에 다시 시도하세요.",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "지원팀에 문의",
|
||||
"Code Highlighting": "코드 하이라이팅",
|
||||
"Enable Highlighting": "하이라이팅 활성화",
|
||||
"Code Language": "코드 언어"
|
||||
"Code Language": "코드 언어",
|
||||
"Top Margin (px)": "위쪽 여백 (px)",
|
||||
"Bottom Margin (px)": "아래쪽 여백 (px)",
|
||||
"Right Margin (px)": "오른쪽 여백 (px)",
|
||||
"Left Margin (px)": "왼쪽 여백 (px)",
|
||||
"Column Gap (%)": "열 간격 (%)"
|
||||
}
|
||||
|
||||
@@ -169,8 +169,6 @@
|
||||
"Hyphenation": "Woordafbreking",
|
||||
"Override Book Layout": "Boekopmaak overschrijven",
|
||||
"Page": "Pagina",
|
||||
"Vertical Margins (px)": "Verticale marges (px)",
|
||||
"Horizontal Margins (%)": "Horizontale marges (%)",
|
||||
"Maximum Number of Columns": "Maximum aantal kolommen",
|
||||
"Maximum Column Height": "Maximale kolomhoogte",
|
||||
"Maximum Column Width": "Maximale kolombreedte",
|
||||
@@ -359,5 +357,10 @@
|
||||
"Contact Support": "Neem contact op met ondersteuning",
|
||||
"Code Highlighting": "Code markering",
|
||||
"Enable Highlighting": "Markering inschakelen",
|
||||
"Code Language": "Code taal"
|
||||
"Code Language": "Code taal",
|
||||
"Top Margin (px)": "Bovenmarge (px)",
|
||||
"Bottom Margin (px)": "Ondermarge (px)",
|
||||
"Right Margin (px)": "Rechter marge (px)",
|
||||
"Left Margin (px)": "Linker marge (px)",
|
||||
"Column Gap (%)": "Kolomafstand (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Twoja biblioteka",
|
||||
"TTS not supported for PDF": "TTS nie jest obsługiwane dla plików PDF",
|
||||
"Override Book Font": "Nadpisz czcionkę książki",
|
||||
"Vertical Margins (px)": "Marginesy pionowe (px)",
|
||||
"Horizontal Margins (%)": "Marginesy poziome (%)",
|
||||
"Apply to All Books": "Zastosuj do wszystkich książek",
|
||||
"Apply to This Book": "Zastosuj do tej książki",
|
||||
"Unable to fetch the translation. Try again later.": "Nie udało się pobrać tłumaczenia. Spróbuj ponownie później.",
|
||||
@@ -365,5 +363,10 @@
|
||||
"Contact Support": "Skontaktuj się z pomocą techniczną",
|
||||
"Code Highlighting": "Podświetlanie kodu",
|
||||
"Enable Highlighting": "Zezwól na podświetlanie",
|
||||
"Code Language": "Język kodu"
|
||||
"Code Language": "Język kodu",
|
||||
"Top Margin (px)": "Górny margines (px)",
|
||||
"Bottom Margin (px)": "Dolny margines (px)",
|
||||
"Right Margin (px)": "Prawy margines (px)",
|
||||
"Left Margin (px)": "Lewy margines (px)",
|
||||
"Column Gap (%)": "Odstęp między kolumnami (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Sua Biblioteca",
|
||||
"TTS not supported for PDF": "TTS não suportado para PDF",
|
||||
"Override Book Font": "Sobrescrever Fonte do Livro",
|
||||
"Vertical Margins (px)": "Margens Verticais (px)",
|
||||
"Horizontal Margins (%)": "Margens Horizontais (%)",
|
||||
"Apply to All Books": "Aplicar a todos os livros",
|
||||
"Apply to This Book": "Aplicar a este livro",
|
||||
"Unable to fetch the translation. Try again later.": "Não foi possível buscar a tradução. Tente novamente mais tarde.",
|
||||
@@ -362,5 +360,10 @@
|
||||
"Contact Support": "Contato com Suporte",
|
||||
"Code Highlighting": "Realce de Código",
|
||||
"Enable Highlighting": "Ativar Realce",
|
||||
"Code Language": "Idioma do Código"
|
||||
"Code Language": "Idioma do Código",
|
||||
"Top Margin (px)": "Margem superior (px)",
|
||||
"Bottom Margin (px)": "Margem inferior (px)",
|
||||
"Right Margin (px)": "Margem direita (px)",
|
||||
"Left Margin (px)": "Margem esquerda (px)",
|
||||
"Column Gap (%)": "Espaço entre colunas (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Ваша библиотека",
|
||||
"TTS not supported for PDF": "TTS не поддерживается для PDF",
|
||||
"Override Book Font": "Переопределить шрифт книги",
|
||||
"Vertical Margins (px)": "Вертикальные поля (px)",
|
||||
"Horizontal Margins (%)": "Горизонтальные поля (%)",
|
||||
"Apply to All Books": "Применить ко всем книгам",
|
||||
"Apply to This Book": "Применить к этой книге",
|
||||
"Unable to fetch the translation. Try again later.": "Не удалось получить перевод. Попробуйте позже.",
|
||||
@@ -365,5 +363,10 @@
|
||||
"Contact Support": "Связаться с поддержкой",
|
||||
"Code Highlighting": "Подсветка кода",
|
||||
"Enable Highlighting": "Включить подсветку",
|
||||
"Code Language": "Язык кода"
|
||||
"Code Language": "Язык кода",
|
||||
"Top Margin (px)": "Верхний отступ (пикс)",
|
||||
"Bottom Margin (px)": "Нижний отступ (пикс)",
|
||||
"Right Margin (px)": "Правый отступ (пикс)",
|
||||
"Left Margin (px)": "Левый отступ (пикс)",
|
||||
"Column Gap (%)": "Промежуток между колонками (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Kütüphaneniz",
|
||||
"TTS not supported for PDF": "PDF için TTS desteklenmiyor",
|
||||
"Override Book Font": "Kitap Yazı Tipini Geçersiz Kıl",
|
||||
"Vertical Margins (px)": "Dikey Kenar Boşlukları (px)",
|
||||
"Horizontal Margins (%)": "Yatay Kenar Boşlukları (%)",
|
||||
"Apply to All Books": "Tüm kitaplara uygula",
|
||||
"Apply to This Book": "Bu kitaba uygula",
|
||||
"Unable to fetch the translation. Try again later.": "Çeviri alınamıyor. Daha sonra tekrar deneyin.",
|
||||
@@ -359,5 +357,10 @@
|
||||
"Contact Support": "Destekle İletişime Geç",
|
||||
"Code Highlighting": "Code Vurgulama",
|
||||
"Enable Highlighting": "Vurgulamayı Etkinleştir",
|
||||
"Code Language": "Code Dili"
|
||||
"Code Language": "Code Dili",
|
||||
"Top Margin (px)": "Üst Kenar Boşluğu (px)",
|
||||
"Bottom Margin (px)": "Alt Kenar Boşluğu (px)",
|
||||
"Right Margin (px)": "Sağ Kenar Boşluğu (px)",
|
||||
"Left Margin (px)": "Sol Kenar Boşluğu (px)",
|
||||
"Column Gap (%)": "Sütun Aralığı (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Ваша бібліотека",
|
||||
"TTS not supported for PDF": "TTS не підтримується для PDF",
|
||||
"Override Book Font": "Перевизначити шрифт книги",
|
||||
"Vertical Margins (px)": "Вертикальні відступи (px)",
|
||||
"Horizontal Margins (%)": "Горизонтальні відступи (%)",
|
||||
"Apply to All Books": "Застосувати до всіх книг",
|
||||
"Apply to This Book": "Застосувати до цієї книги",
|
||||
"Unable to fetch the translation. Try again later.": "Не вдалося отримати переклад. Спробуйте пізніше.",
|
||||
@@ -365,5 +363,10 @@
|
||||
"Contact Support": "Зв'язатися з підтримкою",
|
||||
"Code Highlighting": "Підсвічування коду",
|
||||
"Enable Highlighting": "Увімкнути підсвічування",
|
||||
"Code Language": "Мова коду"
|
||||
"Code Language": "Мова коду",
|
||||
"Top Margin (px)": "Верхній відступ (px)",
|
||||
"Bottom Margin (px)": "Нижній відступ (px)",
|
||||
"Right Margin (px)": "Правий відступ (px)",
|
||||
"Left Margin (px)": "Лівий відступ (px)",
|
||||
"Column Gap (%)": "Проміжок між стовпцями (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "Thư viện của bạn",
|
||||
"TTS not supported for PDF": "TTS không được hỗ trợ cho PDF",
|
||||
"Override Book Font": "Ghi đè phông chữ sách",
|
||||
"Vertical Margins (px)": "Lề dọc (px)",
|
||||
"Horizontal Margins (%)": "Lề ngang (%)",
|
||||
"Apply to All Books": "Áp dụng cho tất cả sách",
|
||||
"Apply to This Book": "Áp dụng cho sách này",
|
||||
"Unable to fetch the translation. Try again later.": "Không thể tải dịch vụ. Vui lòng thử lại sau.",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "Liên hệ hỗ trợ",
|
||||
"Code Highlighting": "Đánh dấu mã",
|
||||
"Enable Highlighting": "Cho phép đánh dấu",
|
||||
"Code Language": "Ngôn ngữ mã"
|
||||
"Code Language": "Ngôn ngữ mã",
|
||||
"Top Margin (px)": "Lề trên (px)",
|
||||
"Bottom Margin (px)": "Lề dưới (px)",
|
||||
"Right Margin (px)": "Lề phải (px)",
|
||||
"Left Margin (px)": "Lề trái (px)",
|
||||
"Column Gap (%)": "Khoảng cách cột (%)"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "书库",
|
||||
"TTS not supported for PDF": "PDF 文档暂不支持 TTS",
|
||||
"Override Book Font": "覆盖书籍字体",
|
||||
"Vertical Margins (px)": "垂直边距(px)",
|
||||
"Horizontal Margins (%)": "水平边距(%)",
|
||||
"Apply to All Books": "应用于所有书籍",
|
||||
"Apply to This Book": "应用于此书籍",
|
||||
"Unable to fetch the translation. Try again later.": "无法获取翻译,请稍后重试。",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "联系客服",
|
||||
"Code Highlighting": "代码高亮",
|
||||
"Enable Highlighting": "启用高亮",
|
||||
"Code Language": "代码语言"
|
||||
"Code Language": "代码语言",
|
||||
"Top Margin (px)": "上边距",
|
||||
"Bottom Margin (px)": "下边距",
|
||||
"Right Margin (px)": "右边距",
|
||||
"Left Margin (px)": "左边距",
|
||||
"Column Gap (%)": "列间距"
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"Your Library": "書庫",
|
||||
"TTS not supported for PDF": "PDF 文檔暫不支持 TTS",
|
||||
"Override Book Font": "覆蓋書籍字體",
|
||||
"Vertical Margins (px)": "垂直邊距(px)",
|
||||
"Horizontal Margins (%)": "水平邊距(%)",
|
||||
"Apply to All Books": "應用於所有書籍",
|
||||
"Apply to This Book": "應用於此書籍",
|
||||
"Unable to fetch the translation. Try again later.": "無法獲取翻譯,請稍後再試。",
|
||||
@@ -356,5 +354,10 @@
|
||||
"Contact Support": "聯繫支援",
|
||||
"Code Highlighting": "程式碼高亮",
|
||||
"Enable Highlighting": "啟用高亮",
|
||||
"Code Language": "程式語言"
|
||||
"Code Language": "程式語言",
|
||||
"Top Margin (px)": "上邊距",
|
||||
"Bottom Margin (px)": "下邊距",
|
||||
"Right Margin (px)": "右邊距",
|
||||
"Left Margin (px)": "左邊距",
|
||||
"Column Gap (%)": "欄間距"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useSafeAreaInsets } from '@/hooks/useSafeAreaInsets';
|
||||
import { getGridTemplate, getInsetEdges } from '@/utils/grid';
|
||||
import { getViewInsets } from '@/utils/insets';
|
||||
import FoliateViewer from './FoliateViewer';
|
||||
import SectionInfo from './SectionInfo';
|
||||
import HeaderBar from './HeaderBar';
|
||||
@@ -33,7 +34,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
const { isSideBarVisible, sideBarBookKey } = useSidebarStore();
|
||||
const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useSettingsStore();
|
||||
|
||||
const insets = useSafeAreaInsets();
|
||||
const screenInsets = useSafeAreaInsets();
|
||||
const aspectRatio = window.innerWidth / window.innerHeight;
|
||||
const gridTemplate = getGridTemplate(bookKeys.length, aspectRatio);
|
||||
|
||||
@@ -45,7 +46,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sideBarBookKey]);
|
||||
|
||||
if (!insets) return null;
|
||||
if (!screenInsets) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -67,7 +68,21 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
const { section, pageinfo, timeinfo, sectionLabel } = progress || {};
|
||||
const isBookmarked = getViewState(bookKey)?.ribbonVisible;
|
||||
const horizontalGapPercent = viewSettings.gapPercent;
|
||||
const verticalMarginPixels = viewSettings.marginPx;
|
||||
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,
|
||||
bottom: gridInsets.bottom + viewInsets.bottom,
|
||||
left: gridInsets.left + viewInsets.left,
|
||||
};
|
||||
const scrolled = viewSettings.scrolled;
|
||||
const showBarsOnScroll = viewSettings.showBarsOnScroll;
|
||||
const showHeader = viewSettings.showHeader && (scrolled ? showBarsOnScroll : true);
|
||||
@@ -95,12 +110,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
bookKey={bookKey}
|
||||
bookDoc={bookDoc}
|
||||
config={config}
|
||||
padding={{
|
||||
top: top ? insets.top : 0,
|
||||
bottom: bottom ? insets.bottom : 0,
|
||||
left: left ? insets.left : 0,
|
||||
right: right ? insets.right : 0,
|
||||
}}
|
||||
insets={contentInsets}
|
||||
/>
|
||||
{viewSettings.vertical && viewSettings.scrolled && (
|
||||
<>
|
||||
@@ -108,8 +118,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
<div
|
||||
className='bg-base-100 absolute left-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
width: `calc(${contentInsets.left + (showFooter ? 32 : 0)}px)`,
|
||||
height: `calc(100%)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -117,8 +127,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
<div
|
||||
className='bg-base-100 absolute right-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
width: `calc(${contentInsets.right + (showHeader ? 32 : 0)}px)`,
|
||||
height: `calc(100%)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -130,7 +140,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
showFooter={showFooter}
|
||||
borderColor={viewSettings.borderColor}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
contentInsets={contentInsets}
|
||||
/>
|
||||
)}
|
||||
{showHeader && (
|
||||
@@ -140,7 +150,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
isScrolled={viewSettings.scrolled}
|
||||
isVertical={viewSettings.vertical}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
contentInsets={contentInsets}
|
||||
gridInsets={gridInsets}
|
||||
/>
|
||||
)}
|
||||
<HintInfo
|
||||
@@ -149,7 +160,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
isScrolled={viewSettings.scrolled}
|
||||
isVertical={viewSettings.vertical}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
contentInsets={contentInsets}
|
||||
gridInsets={gridInsets}
|
||||
/>
|
||||
{showFooter && (
|
||||
<ProgressInfoView
|
||||
@@ -159,7 +171,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
pageinfo={pageinfo}
|
||||
timeinfo={timeinfo}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
contentInsets={contentInsets}
|
||||
gridInsets={gridInsets}
|
||||
/>
|
||||
)}
|
||||
<Annotator bookKey={bookKey} />
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
import { Insets } from '@/types/misc';
|
||||
|
||||
interface DoubleBorderProps {
|
||||
borderColor: string;
|
||||
horizontalGap: number;
|
||||
verticalMargin: number;
|
||||
showHeader: boolean;
|
||||
showFooter: boolean;
|
||||
contentInsets: Insets;
|
||||
}
|
||||
|
||||
const paddingPx = 10;
|
||||
|
||||
const DoubleBorder: React.FC<DoubleBorderProps> = ({
|
||||
borderColor,
|
||||
horizontalGap,
|
||||
verticalMargin,
|
||||
showHeader,
|
||||
showFooter,
|
||||
contentInsets,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{/* outter frame */}
|
||||
<div
|
||||
className={'borderframe pointer-events-none absolute'}
|
||||
style={{
|
||||
border: `4px solid ${borderColor}`,
|
||||
height: `calc(100% - ${verticalMargin * 2}px + ${paddingPx * 2}px)`,
|
||||
top: `calc(${verticalMargin}px - ${paddingPx}px)`,
|
||||
left: `calc(${horizontalGap}% - ${showFooter ? 32 : 0}px - ${paddingPx}px)`,
|
||||
right: `calc(${horizontalGap}% - ${showHeader ? 32 : 0}px - ${paddingPx}px)`,
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px + ${paddingPx * 2}px)`,
|
||||
top: `calc(${contentInsets.top}px - ${paddingPx}px)`,
|
||||
left: `calc(${contentInsets.left}px - ${paddingPx}px)`,
|
||||
right: `calc(${contentInsets.right}px - ${paddingPx}px)`,
|
||||
}}
|
||||
></div>
|
||||
{/* inner frame */}
|
||||
@@ -33,10 +34,10 @@ const DoubleBorder: React.FC<DoubleBorderProps> = ({
|
||||
className={'borderframe pointer-events-none absolute'}
|
||||
style={{
|
||||
border: `1px solid ${borderColor}`,
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
top: `${verticalMargin}px`,
|
||||
left: showFooter ? `${horizontalGap}%` : `calc(${horizontalGap}%)`,
|
||||
right: showHeader ? `${horizontalGap}%` : `calc(${horizontalGap}%)`,
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
top: `${contentInsets.top}px`,
|
||||
left: `calc(${contentInsets.left + (showFooter ? 32 : 0)}px`,
|
||||
right: `calc(${contentInsets.right + (showHeader ? 32 : 0)}px`,
|
||||
}}
|
||||
/>
|
||||
{/* footer */}
|
||||
@@ -48,9 +49,9 @@ const DoubleBorder: React.FC<DoubleBorderProps> = ({
|
||||
borderBottom: `1px solid ${borderColor}`,
|
||||
borderLeft: `1px solid ${borderColor}`,
|
||||
width: '32px',
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
top: `${verticalMargin}px`,
|
||||
left: `calc(${horizontalGap}% - 32px)`,
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
top: `${contentInsets.top}px`,
|
||||
left: `calc(${contentInsets.left}px)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -63,13 +64,13 @@ const DoubleBorder: React.FC<DoubleBorderProps> = ({
|
||||
borderBottom: `1px solid ${borderColor}`,
|
||||
borderRight: `1px solid ${borderColor}`,
|
||||
width: '32px',
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
top: `${verticalMargin}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
top: `${contentInsets.top}px`,
|
||||
left: `calc(100% - ${contentInsets.right}px - 32px)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ const FoliateViewer: React.FC<{
|
||||
bookKey: string;
|
||||
bookDoc: BookDoc;
|
||||
config: BookConfig;
|
||||
padding: { top: number; right: number; bottom: number; left: number };
|
||||
}> = ({ bookKey, bookDoc, config, padding }) => {
|
||||
insets: { top: number; right: number; bottom: number; left: number };
|
||||
}> = ({ bookKey, bookDoc, config, insets }) => {
|
||||
const { getView, setView: setFoliateView, setProgress } = useReaderStore();
|
||||
const { getViewSettings, setViewSettings } = useReaderStore();
|
||||
const { getParallels } = useParallelViewStore();
|
||||
@@ -245,13 +245,16 @@ const FoliateViewer: React.FC<{
|
||||
|
||||
const applyMarginAndGap = () => {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const showHeader = viewSettings.showHeader!;
|
||||
const showFooter = viewSettings.showFooter!;
|
||||
const isCompact = !showHeader && !showFooter;
|
||||
const marginPx = isCompact ? viewSettings.compactMarginPx : viewSettings.marginPx;
|
||||
const gapPercent = isCompact ? viewSettings.compactGapPercent : viewSettings.gapPercent;
|
||||
viewRef.current?.renderer.setAttribute('margin', `${marginPx + padding.top}px`);
|
||||
viewRef.current?.renderer.setAttribute('gap', `${gapPercent}%`);
|
||||
const showDoubleBorder = viewSettings.vertical && viewSettings.doubleBorder;
|
||||
const showDoubleBorderHeader = showDoubleBorder && viewSettings.showHeader;
|
||||
const showDoubleBorderFooter = showDoubleBorder && viewSettings.showFooter;
|
||||
const moreRightInset = showDoubleBorderHeader ? 32 : 0;
|
||||
const moreLeftInset = showDoubleBorderFooter ? 32 : 0;
|
||||
viewRef.current?.renderer.setAttribute('margin-top', `${insets.top}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-left', `${insets.left + moreLeftInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('gap', `${viewSettings.gapPercent}%`);
|
||||
if (viewSettings.scrolled) {
|
||||
viewRef.current?.renderer.setAttribute('flow', 'scrolled');
|
||||
}
|
||||
@@ -271,16 +274,13 @@ const FoliateViewer: React.FC<{
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
padding.top,
|
||||
padding.right,
|
||||
padding.bottom,
|
||||
padding.left,
|
||||
insets.top,
|
||||
insets.right,
|
||||
insets.bottom,
|
||||
insets.left,
|
||||
viewSettings?.doubleBorder,
|
||||
viewSettings?.showHeader,
|
||||
viewSettings?.showFooter,
|
||||
viewSettings?.marginPx,
|
||||
viewSettings?.compactMarginPx,
|
||||
viewSettings?.gapPercent,
|
||||
viewSettings?.compactGapPercent,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -64,9 +64,13 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
};
|
||||
|
||||
const handleMarginChange = (value: number) => {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const marginPx = Math.round((value / 100) * 88);
|
||||
const gapPercent = Math.round((value / 100) * 10);
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx, false, false);
|
||||
viewSettings.marginTopPx = marginPx;
|
||||
viewSettings.marginBottomPx = marginPx / 2;
|
||||
viewSettings.marginLeftPx = marginPx / 2;
|
||||
viewSettings.marginRightPx = marginPx / 2;
|
||||
saveViewSettings(envConfig, bookKey, 'gapPercent', gapPercent, false, false);
|
||||
view?.renderer.setAttribute('margin', `${marginPx}px`);
|
||||
view?.renderer.setAttribute('gap', `${gapPercent}%`);
|
||||
@@ -269,7 +273,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
<div className='flex w-full items-center justify-between gap-x-6'>
|
||||
<Slider
|
||||
initialValue={getMarginProgressValue(
|
||||
viewSettings?.marginPx ?? 44,
|
||||
viewSettings?.marginTopPx ?? 44,
|
||||
viewSettings?.gapPercent ?? 5,
|
||||
)}
|
||||
bubbleElement={<TbBoxMargin size={marginIconSize} />}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
|
||||
interface SectionInfoProps {
|
||||
@@ -8,7 +9,8 @@ interface SectionInfoProps {
|
||||
isScrolled: boolean;
|
||||
isVertical: boolean;
|
||||
horizontalGap: number;
|
||||
verticalMargin: number;
|
||||
contentInsets: Insets;
|
||||
gridInsets: Insets;
|
||||
}
|
||||
|
||||
const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
@@ -17,7 +19,8 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
isScrolled,
|
||||
isVertical,
|
||||
horizontalGap,
|
||||
verticalMargin,
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const [hintMessage, setHintMessage] = React.useState<string | null>(null);
|
||||
const hintTimeout = useRef(2000);
|
||||
@@ -50,14 +53,16 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
'absolute left-0 right-0 top-0 z-10',
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'hintinfo absolute flex items-center justify-end overflow-hidden ps-2',
|
||||
'mt-[env(safe-area-inset-top)]',
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
isVertical ? 'writing-vertical-rl' : 'top-0 h-[44px]',
|
||||
isScrolled
|
||||
@@ -71,11 +76,16 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
bottom: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
bottom: `${contentInsets.bottom * 1.5}px`,
|
||||
right: showDoubleBorder
|
||||
? `calc(${contentInsets.right}px)`
|
||||
: `calc(${Math.max(0, contentInsets.right - 32)}px)`,
|
||||
width: showDoubleBorder ? '30px' : `${horizontalGap}%`,
|
||||
}
|
||||
: { insetInlineEnd: `${horizontalGap}%` }
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
insetInlineEnd: `calc(${horizontalGap / 2}% + ${contentInsets.right}px)`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<h2 className={clsx('text-neutral-content text-center font-sans text-xs font-light')}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
@@ -12,7 +13,8 @@ interface PageInfoProps {
|
||||
pageinfo?: PageInfo;
|
||||
timeinfo?: TimeInfo;
|
||||
horizontalGap: number;
|
||||
verticalMargin: number;
|
||||
contentInsets: Insets;
|
||||
gridInsets: Insets;
|
||||
}
|
||||
|
||||
const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
@@ -22,7 +24,8 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
pageinfo,
|
||||
timeinfo,
|
||||
horizontalGap,
|
||||
verticalMargin,
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const _ = useTranslation();
|
||||
const { appService } = useEnv();
|
||||
@@ -51,25 +54,25 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'pageinfo absolute bottom-0 flex items-center justify-between',
|
||||
'progressinfo absolute bottom-0 flex items-center justify-between',
|
||||
'text-neutral-content font-sans text-xs font-extralight',
|
||||
isVertical ? 'writing-vertical-rl' : 'h-12 w-full',
|
||||
isVertical ? 'writing-vertical-rl' : 'h-[52px] w-full',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
bottom: `${verticalMargin * 1.5}px`,
|
||||
left: showDoubleBorder ? `calc(${horizontalGap}% - 32px)` : 0,
|
||||
bottom: `${contentInsets.bottom * 1.5}px`,
|
||||
left: showDoubleBorder
|
||||
? `calc(${contentInsets.left}px)`
|
||||
: `calc(${Math.max(0, contentInsets.left - 32)}px)`,
|
||||
width: showDoubleBorder ? '32px' : `${horizontalGap}%`,
|
||||
height: `calc(100% - ${verticalMargin * 3}px)`,
|
||||
height: `calc(100% - ${((contentInsets.top + contentInsets.bottom) / 2) * 3}px)`,
|
||||
}
|
||||
: {
|
||||
paddingInlineStart: `${horizontalGap}%`,
|
||||
paddingInlineEnd: `${horizontalGap}%`,
|
||||
paddingBottom: appService?.hasSafeAreaInset
|
||||
? 'calc(env(safe-area-inset-bottom)*0.67)'
|
||||
: 0,
|
||||
paddingInlineStart: `calc(${horizontalGap / 2}% + ${contentInsets.left}px)`,
|
||||
paddingInlineEnd: `calc(${horizontalGap / 2}% + ${contentInsets.right}px)`,
|
||||
paddingBottom: appService?.hasSafeAreaInset ? `${gridInsets.bottom * 0.67}px` : 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
|
||||
interface SectionInfoProps {
|
||||
section?: string;
|
||||
@@ -7,7 +8,8 @@ interface SectionInfoProps {
|
||||
isScrolled: boolean;
|
||||
isVertical: boolean;
|
||||
horizontalGap: number;
|
||||
verticalMargin: number;
|
||||
contentInsets: Insets;
|
||||
gridInsets: Insets;
|
||||
}
|
||||
|
||||
const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
@@ -16,32 +18,41 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
isScrolled,
|
||||
isVertical,
|
||||
horizontalGap,
|
||||
verticalMargin,
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
'absolute left-0 right-0 top-0 z-10',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'sectioninfo absolute flex items-center overflow-hidden',
|
||||
'mt-[env(safe-area-inset-top)]',
|
||||
isVertical ? 'writing-vertical-rl max-h-[85%]' : 'top-0 h-[44px]',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
top: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
top: `${contentInsets.top * 1.5}px`,
|
||||
right: showDoubleBorder
|
||||
? `calc(${contentInsets.right}px)`
|
||||
: `calc(${Math.max(0, contentInsets.right - 32)}px)`,
|
||||
width: showDoubleBorder ? '32px' : `${horizontalGap}%`,
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
}
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
insetInlineStart: `calc(${horizontalGap / 2}% + ${contentInsets.left}px)`,
|
||||
width: `calc(100% - ${contentInsets.left + contentInsets.right}px)`,
|
||||
}
|
||||
: { insetInlineStart: `${horizontalGap}%`, width: `calc(100% - ${horizontalGap * 2}%)` }
|
||||
}
|
||||
>
|
||||
<h2
|
||||
|
||||
@@ -31,10 +31,23 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [textIndent, setTextIndent] = useState(viewSettings.textIndent!);
|
||||
const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification!);
|
||||
const [hyphenation, setHyphenation] = useState(viewSettings.hyphenation!);
|
||||
const [marginPx, setMarginPx] = useState(viewSettings.marginPx!);
|
||||
const [marginTopPx, setMarginTopPx] = useState(
|
||||
viewSettings.marginPx || viewSettings.marginTopPx!,
|
||||
);
|
||||
const [marginBottomPx, setMarginBottomPx] = useState(viewSettings.marginBottomPx!);
|
||||
const [marginLeftPx, setMarginLeftPx] = useState(viewSettings.marginLeftPx!);
|
||||
const [marginRightPx, setMarginRightPx] = useState(viewSettings.marginRightPx!);
|
||||
const [compactMarginTopPx, setCompactMarginTopPx] = useState(
|
||||
viewSettings.compactMarginPx || viewSettings.compactMarginTopPx!,
|
||||
);
|
||||
const [compactMarginBottomPx, setCompactMarginBottomPx] = useState(
|
||||
viewSettings.compactMarginBottomPx!,
|
||||
);
|
||||
const [gapPercent, setGapPercent] = useState(viewSettings.gapPercent!);
|
||||
const [compactMarginPx, setCompactMarginPx] = useState(viewSettings.compactMarginPx!);
|
||||
const [compactGapPercent, setCompactGapPercent] = useState(viewSettings.compactGapPercent!);
|
||||
const [compactMarginLeftPx, setCompactMarginLeftPx] = useState(viewSettings.compactMarginLeftPx!);
|
||||
const [compactMarginRightPx, setCompactMarginRightPx] = useState(
|
||||
viewSettings.compactMarginRightPx!,
|
||||
);
|
||||
const [maxColumnCount, setMaxColumnCount] = useState(viewSettings.maxColumnCount!);
|
||||
const [maxInlineSize, setMaxInlineSize] = useState(viewSettings.maxInlineSize!);
|
||||
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize!);
|
||||
@@ -84,18 +97,75 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
}, [hyphenation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (marginPx === viewSettings.marginPx) return;
|
||||
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx, false, false);
|
||||
view?.renderer.setAttribute('margin', `${marginPx}px`);
|
||||
if (marginTopPx === viewSettings.marginTopPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
saveViewSettings(envConfig, bookKey, 'marginTopPx', marginTopPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [marginPx]);
|
||||
}, [marginTopPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginPx === viewSettings.compactMarginPx) return;
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginPx', compactMarginPx, false, false);
|
||||
view?.renderer.setAttribute('margin', `${compactMarginPx}px`);
|
||||
if (marginBottomPx === viewSettings.marginBottomPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
saveViewSettings(envConfig, bookKey, 'marginBottomPx', marginBottomPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginPx]);
|
||||
}, [marginBottomPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (marginRightPx === viewSettings.marginRightPx) return;
|
||||
viewSettings.marginPx = undefined;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
saveViewSettings(
|
||||
envConfig,
|
||||
bookKey,
|
||||
'compactMarginBottomPx',
|
||||
compactMarginBottomPx,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginBottomPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginRightPx === viewSettings.compactMarginRightPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
saveViewSettings(
|
||||
envConfig,
|
||||
bookKey,
|
||||
'compactMarginRightPx',
|
||||
compactMarginRightPx,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
console.log('compactMarginRightPx', compactMarginRightPx, viewSettings.compactMarginRightPx);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginRightPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactMarginLeftPx === viewSettings.compactMarginLeftPx) return;
|
||||
viewSettings.compactMarginPx = undefined;
|
||||
saveViewSettings(envConfig, bookKey, 'compactMarginLeftPx', compactMarginLeftPx, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactMarginLeftPx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gapPercent === viewSettings.gapPercent) return;
|
||||
@@ -107,16 +177,6 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [gapPercent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (compactGapPercent === viewSettings.compactGapPercent) return;
|
||||
saveViewSettings(envConfig, bookKey, 'compactGapPercent', compactGapPercent, false, false);
|
||||
view?.renderer.setAttribute('gap', `${compactGapPercent}%`);
|
||||
if (viewSettings.scrolled) {
|
||||
view?.renderer.setAttribute('flow', 'scrolled');
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compactGapPercent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (maxColumnCount === viewSettings.maxColumnCount) return;
|
||||
saveViewSettings(envConfig, bookKey, 'maxColumnCount', maxColumnCount, false, false);
|
||||
@@ -171,14 +231,6 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (doubleBorder === viewSettings.doubleBorder) return;
|
||||
if (doubleBorder && viewSettings.vertical) {
|
||||
viewSettings.gapPercent = Math.max(
|
||||
viewSettings.gapPercent,
|
||||
Math.ceil(4800 / window.innerWidth),
|
||||
);
|
||||
setGapPercent(viewSettings.gapPercent);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'doubleBorder', doubleBorder, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [doubleBorder]);
|
||||
@@ -206,15 +258,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showHeader === viewSettings.showHeader) return;
|
||||
if (showHeader && !viewSettings.vertical) {
|
||||
viewSettings.marginPx = Math.max(viewSettings.marginPx, 44);
|
||||
setMarginPx(viewSettings.marginPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
} else if (showHeader && viewSettings.vertical) {
|
||||
viewSettings.gapPercent = Math.max(
|
||||
viewSettings.gapPercent,
|
||||
Math.ceil(4800 / window.innerWidth),
|
||||
);
|
||||
setGapPercent(viewSettings.gapPercent);
|
||||
viewSettings.marginTopPx = Math.max(viewSettings.marginTopPx, 44);
|
||||
setMarginTopPx(viewSettings.marginTopPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'showHeader', showHeader, false, false);
|
||||
@@ -225,15 +270,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showFooter === viewSettings.showFooter) return;
|
||||
if (showFooter && !viewSettings.vertical) {
|
||||
viewSettings.marginPx = Math.max(viewSettings.marginPx, 44);
|
||||
setMarginPx(viewSettings.marginPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
} else if (showFooter && viewSettings.vertical) {
|
||||
viewSettings.gapPercent = Math.max(
|
||||
viewSettings.gapPercent,
|
||||
Math.ceil(4800 / window.innerWidth),
|
||||
);
|
||||
setGapPercent(viewSettings.gapPercent);
|
||||
viewSettings.marginBottomPx = Math.max(viewSettings.marginBottomPx, 44);
|
||||
setMarginBottomPx(viewSettings.marginBottomPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
saveViewSettings(envConfig, bookKey, 'showFooter', showFooter, false, false);
|
||||
@@ -243,6 +281,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language);
|
||||
const mightBeRTLBook = MIGHT_BE_RTL_LANGS.includes(langCode) || isCJKEnv();
|
||||
const isVertical = viewSettings.vertical || writingMode.includes('vertical');
|
||||
|
||||
return (
|
||||
<div className='my-4 w-full space-y-6'>
|
||||
@@ -405,22 +444,42 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
label={_('Vertical Margins (px)')}
|
||||
value={showFooter || showHeader ? marginPx : compactMarginPx}
|
||||
onChange={showFooter || showHeader ? setMarginPx : setCompactMarginPx}
|
||||
min={!viewSettings.vertical && (showFooter || showHeader) ? 44 : 0}
|
||||
label={_('Top Margin (px)')}
|
||||
value={showHeader && !isVertical ? marginTopPx : compactMarginTopPx}
|
||||
onChange={showHeader && !isVertical ? setMarginTopPx : setCompactMarginTopPx}
|
||||
min={0}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
<NumberInput
|
||||
label={_('Horizontal Margins (%)')}
|
||||
value={showFooter || showHeader ? gapPercent : compactGapPercent}
|
||||
onChange={showFooter || showHeader ? setGapPercent : setCompactGapPercent}
|
||||
min={
|
||||
viewSettings.vertical && (showFooter || showHeader)
|
||||
? Math.ceil(4800 / window.innerWidth)
|
||||
: 0
|
||||
}
|
||||
label={_('Bottom Margin (px)')}
|
||||
value={showFooter && !isVertical ? marginBottomPx : compactMarginBottomPx}
|
||||
onChange={showFooter && !isVertical ? setMarginBottomPx : setCompactMarginBottomPx}
|
||||
min={0}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
<NumberInput
|
||||
label={_('Right Margin (px)')}
|
||||
value={showHeader && isVertical ? marginRightPx : compactMarginRightPx}
|
||||
onChange={showHeader && isVertical ? setMarginRightPx : setCompactMarginRightPx}
|
||||
min={0}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
<NumberInput
|
||||
label={_('Left Margin (px)')}
|
||||
value={showFooter && isVertical ? marginLeftPx : compactMarginLeftPx}
|
||||
onChange={showFooter && isVertical ? setMarginLeftPx : setCompactMarginLeftPx}
|
||||
min={0}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
<NumberInput
|
||||
label={_('Column Gap (%)')}
|
||||
value={gapPercent}
|
||||
onChange={setGapPercent}
|
||||
min={0}
|
||||
max={30}
|
||||
/>
|
||||
<NumberInput
|
||||
|
||||
@@ -87,10 +87,15 @@ export const DEFAULT_BOOK_FONT: BookFont = {
|
||||
};
|
||||
|
||||
export const DEFAULT_BOOK_LAYOUT: BookLayout = {
|
||||
marginPx: 44,
|
||||
marginTopPx: 44,
|
||||
marginBottomPx: 16,
|
||||
marginLeftPx: 16,
|
||||
marginRightPx: 16,
|
||||
compactMarginTopPx: 16,
|
||||
compactMarginBottomPx: 16,
|
||||
compactMarginLeftPx: 16,
|
||||
compactMarginRightPx: 16,
|
||||
gapPercent: 5,
|
||||
compactMarginPx: 0,
|
||||
compactGapPercent: 5,
|
||||
scrolled: false,
|
||||
disableClick: false,
|
||||
swapClickArea: false,
|
||||
|
||||
@@ -80,16 +80,22 @@ export class TTSController extends EventTarget {
|
||||
|
||||
#getHighlighter(options: TTSHighlightOptions) {
|
||||
return (range: Range) => {
|
||||
const { style, color } = options;
|
||||
const { overlayer } = this.view.renderer.getContents()[0] as { overlayer: Overlayer };
|
||||
const { style, color } = options;
|
||||
overlayer?.remove(HIGHLIGHT_KEY);
|
||||
overlayer?.add(HIGHLIGHT_KEY, range, Overlayer[style], { color });
|
||||
this.view.renderer.scrollToAnchor(range);
|
||||
const rect = range.getBoundingClientRect();
|
||||
const { start, size, viewSize, sideProp } = this.view.renderer;
|
||||
const position = rect[sideProp === 'height' ? 'y' : 'x'] + 88;
|
||||
const offset = this.view.book.dir === 'rtl' ? viewSize - position : position;
|
||||
if (!this.view.renderer.scrolled || offset < start || offset > start + size) {
|
||||
this.view.renderer.scrollToAnchor(range);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#clearHighlighter() {
|
||||
const { overlayer } = this.view.renderer.getContents()[0] as { overlayer: Overlayer };
|
||||
const { overlayer } = (this.view.renderer.getContents()?.[0] || {}) as { overlayer: Overlayer };
|
||||
overlayer?.remove(HIGHLIGHT_KEY);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,10 +73,17 @@ export interface BooknoteGroup {
|
||||
export type WritingMode = 'auto' | 'horizontal-tb' | 'horizontal-rl' | 'vertical-rl';
|
||||
|
||||
export interface BookLayout {
|
||||
marginPx: number;
|
||||
marginTopPx: number;
|
||||
marginBottomPx: number;
|
||||
marginLeftPx: number;
|
||||
marginRightPx: number;
|
||||
marginPx?: number; // deprecated
|
||||
compactMarginTopPx: number;
|
||||
compactMarginBottomPx: number;
|
||||
compactMarginLeftPx: number;
|
||||
compactMarginRightPx: number;
|
||||
compactMarginPx?: number; // deprecated
|
||||
gapPercent: number;
|
||||
compactMarginPx: number;
|
||||
compactGapPercent: number;
|
||||
scrolled: boolean;
|
||||
disableClick: boolean;
|
||||
swapClickArea: boolean;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export type Insets = {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
};
|
||||
@@ -41,6 +41,7 @@ export interface FoliateView extends HTMLElement {
|
||||
start: number;
|
||||
end: number;
|
||||
containerPosition: number;
|
||||
sideProp: 'width' | 'height';
|
||||
setAttribute: (name: string, value: string | number) => void;
|
||||
removeAttribute: (name: string) => void;
|
||||
next: () => Promise<void>;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Insets } from '@/types/misc';
|
||||
import { ViewSettings } from '@/types/book';
|
||||
|
||||
export const getViewInsets = (viewSettings: ViewSettings) => {
|
||||
const showHeader = viewSettings.showHeader!;
|
||||
const showFooter = viewSettings.showFooter!;
|
||||
const isVertical = viewSettings.vertical || viewSettings.writingMode.includes('vertical');
|
||||
const fullMarginTopPx = viewSettings.marginPx || viewSettings.marginTopPx;
|
||||
const compactMarginTopPx = viewSettings.compactMarginPx || viewSettings.compactMarginTopPx;
|
||||
const fullMarginBottomPx = viewSettings.marginBottomPx;
|
||||
const compactMarginBottomPx = viewSettings.compactMarginBottomPx;
|
||||
const fullMarginLeftPx = viewSettings.marginLeftPx;
|
||||
const fullMarginRightPx = viewSettings.marginRightPx;
|
||||
const compactMarginLeftPx = viewSettings.compactMarginLeftPx;
|
||||
const compactMarginRightPx = viewSettings.compactMarginRightPx;
|
||||
|
||||
return {
|
||||
top: showHeader && !isVertical ? fullMarginTopPx : compactMarginTopPx,
|
||||
right: showHeader && isVertical ? fullMarginRightPx : compactMarginRightPx,
|
||||
bottom: showFooter && !isVertical ? fullMarginBottomPx : compactMarginBottomPx,
|
||||
left: showFooter && isVertical ? fullMarginLeftPx : compactMarginLeftPx,
|
||||
} as Insets;
|
||||
};
|
||||
@@ -241,6 +241,11 @@ const getLayoutStyles = (
|
||||
${!vertical && overrideLayout ? `margin-bottom: ${paragraphMargin}em !important;` : ''}
|
||||
}
|
||||
|
||||
:lang(zh), :lang(ja), :lang(ko) {
|
||||
widows: 1;
|
||||
orphans: 1;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
@@ -423,8 +428,8 @@ export const transformStylesheet = (
|
||||
) => {
|
||||
const isMobile = ['ios', 'android'].includes(getOSPlatform());
|
||||
const fontScale = isMobile ? 1.25 : 1;
|
||||
const w = width * (1 - viewSettings.gapPercent / 100);
|
||||
const h = height - viewSettings.marginPx * 2;
|
||||
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);
|
||||
|
||||
+1
-1
Submodule packages/foliate-js updated: 97f9879568...fdde2bf6ed
Reference in New Issue
Block a user