forked from akai/readest
This commit is contained in:
@@ -167,6 +167,30 @@ const handleReset = () => {
|
||||
|
||||
The hook resolves the default by reading from `getDefaultViewSettings(ctx)` and calls each provided setter with that value, which then fires your `useEffect` and persists the change.
|
||||
|
||||
### Step 5 — Register in the Command Palette
|
||||
|
||||
If your setting has a visible row in a panel, register it in the matching `*PanelItems` array in `src/services/commandRegistry.ts`. This wires it into the command-palette fuzzy search so users can jump straight to it.
|
||||
|
||||
```ts
|
||||
// src/services/commandRegistry.ts
|
||||
const layoutPanelItems = [
|
||||
// ...existing entries
|
||||
{
|
||||
id: 'settings.layout.myNewToggle',
|
||||
labelKey: _('My New Toggle'),
|
||||
keywords: ['search', 'terms', 'for', 'discoverability'],
|
||||
section: 'Paragraph',
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
- `id` must match the `data-setting-id` attribute on the panel row. The palette uses it to scroll/highlight the target control.
|
||||
- `labelKey` uses `stubTranslation` (imported as `_`) so the extractor picks it up — the same string that appears in the panel.
|
||||
- `keywords` broadens fuzzy-search hits beyond the label; include synonyms, related jargon, and the panel section name.
|
||||
- `section` groups the entry in the palette results (matches the panel's sub-header: `Layout`, `Paragraph`, `Page`, `Header & Footer`, etc.).
|
||||
|
||||
Skip this step only for settings that don't surface as a user-visible row (hidden toggles, flags used by other settings).
|
||||
|
||||
### Don'ts
|
||||
|
||||
- **Don't make the field optional** just to skip providing a default. Add a default in Step 2 instead.
|
||||
@@ -182,3 +206,4 @@ The hook resolves the default by reading from `getDefaultViewSettings(ctx)` and
|
||||
- [ ] Read via `getViewSettings(bookKey) || settings.globalViewSettings`
|
||||
- [ ] Write via `saveViewSettings(envConfig, bookKey, 'key', value)`
|
||||
- [ ] Reset setter wired into `useResetViewSettings` if the panel has a reset menu
|
||||
- [ ] Command-palette entry added to the matching `*PanelItems` array in `src/services/commandRegistry.ts`, with an `id` that matches the panel row's `data-setting-id`
|
||||
|
||||
@@ -1242,5 +1242,6 @@
|
||||
"Unavailable": "غير متاح",
|
||||
"Remove": "إزالة",
|
||||
"Edit OPDS Catalog": "تعديل كتالوج OPDS",
|
||||
"Save Changes": "حفظ التغييرات"
|
||||
"Save Changes": "حفظ التغييرات",
|
||||
"Use Book Layout": "استخدام تخطيط الكتاب"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "অনুপলব্ধ",
|
||||
"Remove": "সরান",
|
||||
"Edit OPDS Catalog": "OPDS ক্যাটালগ সম্পাদনা",
|
||||
"Save Changes": "পরিবর্তন সংরক্ষণ"
|
||||
"Save Changes": "পরিবর্তন সংরক্ষণ",
|
||||
"Use Book Layout": "বইয়ের লেআউট ব্যবহার করুন"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "རག་མི་ཐུབ",
|
||||
"Remove": "བསུབ།",
|
||||
"Edit OPDS Catalog": "OPDS དཀར་ཆག་རྩོམ་སྒྲིག",
|
||||
"Save Changes": "བསྒྱུར་བཅོས་ཉར་ཚགས།"
|
||||
"Save Changes": "བསྒྱུར་བཅོས་ཉར་ཚགས།",
|
||||
"Use Book Layout": "དཔེ་ཆའི་བཀོད་སྒྲིག་སྤྱོད་པ"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Nicht verfügbar",
|
||||
"Remove": "Entfernen",
|
||||
"Edit OPDS Catalog": "OPDS-Katalog bearbeiten",
|
||||
"Save Changes": "Änderungen speichern"
|
||||
"Save Changes": "Änderungen speichern",
|
||||
"Use Book Layout": "Buchlayout verwenden"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Μη διαθέσιμο",
|
||||
"Remove": "Αφαίρεση",
|
||||
"Edit OPDS Catalog": "Επεξεργασία καταλόγου OPDS",
|
||||
"Save Changes": "Αποθήκευση αλλαγών"
|
||||
"Save Changes": "Αποθήκευση αλλαγών",
|
||||
"Use Book Layout": "Χρήση διάταξης βιβλίου"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "No disponible",
|
||||
"Remove": "Eliminar",
|
||||
"Edit OPDS Catalog": "Editar catálogo OPDS",
|
||||
"Save Changes": "Guardar cambios"
|
||||
"Save Changes": "Guardar cambios",
|
||||
"Use Book Layout": "Usar el diseño del libro"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "ناموجود",
|
||||
"Remove": "حذف",
|
||||
"Edit OPDS Catalog": "ویرایش کاتالوگ OPDS",
|
||||
"Save Changes": "ذخیره تغییرات"
|
||||
"Save Changes": "ذخیره تغییرات",
|
||||
"Use Book Layout": "استفاده از چیدمان کتاب"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "Indisponible",
|
||||
"Remove": "Supprimer",
|
||||
"Edit OPDS Catalog": "Modifier le catalogue OPDS",
|
||||
"Save Changes": "Enregistrer les modifications"
|
||||
"Save Changes": "Enregistrer les modifications",
|
||||
"Use Book Layout": "Utiliser la mise en page du livre"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "לא זמין",
|
||||
"Remove": "הסר",
|
||||
"Edit OPDS Catalog": "עריכת קטלוג OPDS",
|
||||
"Save Changes": "שמור שינויים"
|
||||
"Save Changes": "שמור שינויים",
|
||||
"Use Book Layout": "השתמש בפריסת הספר"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "अनुपलब्ध",
|
||||
"Remove": "हटाएँ",
|
||||
"Edit OPDS Catalog": "OPDS कैटलॉग संपादित करें",
|
||||
"Save Changes": "परिवर्तन सहेजें"
|
||||
"Save Changes": "परिवर्तन सहेजें",
|
||||
"Use Book Layout": "पुस्तक का लेआउट उपयोग करें"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Nem elérhető",
|
||||
"Remove": "Eltávolítás",
|
||||
"Edit OPDS Catalog": "OPDS katalógus szerkesztése",
|
||||
"Save Changes": "Módosítások mentése"
|
||||
"Save Changes": "Módosítások mentése",
|
||||
"Use Book Layout": "Könyv elrendezésének használata"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "Tidak Tersedia",
|
||||
"Remove": "Hapus",
|
||||
"Edit OPDS Catalog": "Edit Katalog OPDS",
|
||||
"Save Changes": "Simpan Perubahan"
|
||||
"Save Changes": "Simpan Perubahan",
|
||||
"Use Book Layout": "Gunakan tata letak buku"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "Non disponibile",
|
||||
"Remove": "Rimuovi",
|
||||
"Edit OPDS Catalog": "Modifica catalogo OPDS",
|
||||
"Save Changes": "Salva modifiche"
|
||||
"Save Changes": "Salva modifiche",
|
||||
"Use Book Layout": "Usa layout del libro"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "利用不可",
|
||||
"Remove": "削除",
|
||||
"Edit OPDS Catalog": "OPDSカタログを編集",
|
||||
"Save Changes": "変更を保存"
|
||||
"Save Changes": "変更を保存",
|
||||
"Use Book Layout": "書籍のレイアウトを使用"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "사용 불가",
|
||||
"Remove": "삭제",
|
||||
"Edit OPDS Catalog": "OPDS 카탈로그 편집",
|
||||
"Save Changes": "변경 사항 저장"
|
||||
"Save Changes": "변경 사항 저장",
|
||||
"Use Book Layout": "책 레이아웃 사용"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "Tidak Tersedia",
|
||||
"Remove": "Alih keluar",
|
||||
"Edit OPDS Catalog": "Edit Katalog OPDS",
|
||||
"Save Changes": "Simpan Perubahan"
|
||||
"Save Changes": "Simpan Perubahan",
|
||||
"Use Book Layout": "Guna susun atur buku"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Niet beschikbaar",
|
||||
"Remove": "Verwijderen",
|
||||
"Edit OPDS Catalog": "OPDS-catalogus bewerken",
|
||||
"Save Changes": "Wijzigingen opslaan"
|
||||
"Save Changes": "Wijzigingen opslaan",
|
||||
"Use Book Layout": "Boekindeling gebruiken"
|
||||
}
|
||||
|
||||
@@ -1216,5 +1216,6 @@
|
||||
"Unavailable": "Niedostępne",
|
||||
"Remove": "Usuń",
|
||||
"Edit OPDS Catalog": "Edytuj katalog OPDS",
|
||||
"Save Changes": "Zapisz zmiany"
|
||||
"Save Changes": "Zapisz zmiany",
|
||||
"Use Book Layout": "Użyj układu książki"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "Indisponível",
|
||||
"Remove": "Remover",
|
||||
"Edit OPDS Catalog": "Editar catálogo OPDS",
|
||||
"Save Changes": "Salvar alterações"
|
||||
"Save Changes": "Salvar alterações",
|
||||
"Use Book Layout": "Usar layout do livro"
|
||||
}
|
||||
|
||||
@@ -1203,5 +1203,6 @@
|
||||
"Unavailable": "Indisponibil",
|
||||
"Remove": "Elimină",
|
||||
"Edit OPDS Catalog": "Editare catalog OPDS",
|
||||
"Save Changes": "Salvare modificări"
|
||||
"Save Changes": "Salvare modificări",
|
||||
"Use Book Layout": "Folosește aspectul cărții"
|
||||
}
|
||||
|
||||
@@ -1216,5 +1216,6 @@
|
||||
"Unavailable": "Недоступно",
|
||||
"Remove": "Удалить",
|
||||
"Edit OPDS Catalog": "Редактировать каталог OPDS",
|
||||
"Save Changes": "Сохранить изменения"
|
||||
"Save Changes": "Сохранить изменения",
|
||||
"Use Book Layout": "Использовать макет книги"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "ලබාගත නොහැක",
|
||||
"Remove": "ඉවත් කරන්න",
|
||||
"Edit OPDS Catalog": "OPDS නාමාවලිය සංස්කරණය",
|
||||
"Save Changes": "වෙනස්කම් සුරකින්න"
|
||||
"Save Changes": "වෙනස්කම් සුරකින්න",
|
||||
"Use Book Layout": "පොතේ සැකැස්ම භාවිත කරන්න"
|
||||
}
|
||||
|
||||
@@ -1216,5 +1216,6 @@
|
||||
"Unavailable": "Nedosegljivo",
|
||||
"Remove": "Odstrani",
|
||||
"Edit OPDS Catalog": "Uredi katalog OPDS",
|
||||
"Save Changes": "Shrani spremembe"
|
||||
"Save Changes": "Shrani spremembe",
|
||||
"Use Book Layout": "Uporabi postavitev knjige"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Inte tillgängligt",
|
||||
"Remove": "Ta bort",
|
||||
"Edit OPDS Catalog": "Redigera OPDS-katalog",
|
||||
"Save Changes": "Spara ändringar"
|
||||
"Save Changes": "Spara ändringar",
|
||||
"Use Book Layout": "Använd bokens layout"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "கிடைக்கவில்லை",
|
||||
"Remove": "நீக்கு",
|
||||
"Edit OPDS Catalog": "OPDS பட்டியலைத் திருத்து",
|
||||
"Save Changes": "மாற்றங்களைச் சேமி"
|
||||
"Save Changes": "மாற்றங்களைச் சேமி",
|
||||
"Use Book Layout": "புத்தகத்தின் அமைப்பைப் பயன்படுத்து"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "ไม่พร้อมใช้งาน",
|
||||
"Remove": "ลบ",
|
||||
"Edit OPDS Catalog": "แก้ไขแคตตาล็อก OPDS",
|
||||
"Save Changes": "บันทึกการเปลี่ยนแปลง"
|
||||
"Save Changes": "บันทึกการเปลี่ยนแปลง",
|
||||
"Use Book Layout": "ใช้เลย์เอาต์ของหนังสือ"
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,6 @@
|
||||
"Unavailable": "Kullanılamaz",
|
||||
"Remove": "Kaldır",
|
||||
"Edit OPDS Catalog": "OPDS Kataloğunu Düzenle",
|
||||
"Save Changes": "Değişiklikleri Kaydet"
|
||||
"Save Changes": "Değişiklikleri Kaydet",
|
||||
"Use Book Layout": "Kitap düzenini kullan"
|
||||
}
|
||||
|
||||
@@ -1216,5 +1216,6 @@
|
||||
"Unavailable": "Недоступно",
|
||||
"Remove": "Видалити",
|
||||
"Edit OPDS Catalog": "Редагувати каталог OPDS",
|
||||
"Save Changes": "Зберегти зміни"
|
||||
"Save Changes": "Зберегти зміни",
|
||||
"Use Book Layout": "Використовувати макет книги"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "Không khả dụng",
|
||||
"Remove": "Xóa",
|
||||
"Edit OPDS Catalog": "Chỉnh sửa danh mục OPDS",
|
||||
"Save Changes": "Lưu thay đổi"
|
||||
"Save Changes": "Lưu thay đổi",
|
||||
"Use Book Layout": "Sử dụng bố cục sách"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "不可用",
|
||||
"Remove": "移除",
|
||||
"Edit OPDS Catalog": "编辑 OPDS 目录",
|
||||
"Save Changes": "保存更改"
|
||||
"Save Changes": "保存更改",
|
||||
"Use Book Layout": "使用书籍排版"
|
||||
}
|
||||
|
||||
@@ -1177,5 +1177,6 @@
|
||||
"Unavailable": "無法使用",
|
||||
"Remove": "移除",
|
||||
"Edit OPDS Catalog": "編輯 OPDS 目錄",
|
||||
"Save Changes": "儲存變更"
|
||||
"Save Changes": "儲存變更",
|
||||
"Use Book Layout": "使用書籍排版"
|
||||
}
|
||||
|
||||
@@ -368,6 +368,58 @@ describe('getLayoutStyles branches (via getStyles)', () => {
|
||||
// getStyles passes 1.0 as zoomLevel to getLayoutStyles
|
||||
expect(css).toContain('zoom: 1');
|
||||
});
|
||||
|
||||
it('omits only paragraph-related layout rules when useBookLayout is true', () => {
|
||||
const vs = makeViewSettings({
|
||||
useBookLayout: true,
|
||||
lineHeight: 1.7,
|
||||
wordSpacing: 3,
|
||||
letterSpacing: 2,
|
||||
textIndent: 2,
|
||||
paragraphMargin: 1.25,
|
||||
fullJustification: true,
|
||||
hyphenation: true,
|
||||
marginTopPx: 50,
|
||||
});
|
||||
const css = getStyles(vs, theme);
|
||||
// paragraph-specific tokens driven by the Paragraph section must be absent
|
||||
expect(css).not.toContain('--default-text-align:');
|
||||
expect(css).not.toContain('line-height: 1.7');
|
||||
expect(css).not.toContain('word-spacing: 3px');
|
||||
expect(css).not.toContain('letter-spacing: 2px');
|
||||
expect(css).not.toContain('text-indent: 2em');
|
||||
expect(css).not.toContain('hyphens: auto');
|
||||
expect(css).not.toContain('-webkit-hyphens: auto');
|
||||
// non-paragraph layout rules must still be emitted
|
||||
expect(css).toContain('@namespace epub');
|
||||
expect(css).toContain('--margin-top: 50px');
|
||||
expect(css).toContain('--margin-right:');
|
||||
expect(css).toContain('--margin-bottom:');
|
||||
expect(css).toContain('--margin-left:');
|
||||
// font/color/translation sections must still be present
|
||||
expect(css).toContain('--serif:');
|
||||
expect(css).toContain('--theme-bg-color');
|
||||
expect(css).toContain('.translation-source');
|
||||
});
|
||||
|
||||
it('includes paragraph layout rules when useBookLayout is false', () => {
|
||||
const vs = makeViewSettings({
|
||||
useBookLayout: false,
|
||||
lineHeight: 1.7,
|
||||
wordSpacing: 3,
|
||||
letterSpacing: 2,
|
||||
textIndent: 2,
|
||||
fullJustification: true,
|
||||
hyphenation: true,
|
||||
});
|
||||
const css = getStyles(vs, theme);
|
||||
expect(css).toContain('--default-text-align: justify');
|
||||
expect(css).toContain('line-height: 1.7');
|
||||
expect(css).toContain('word-spacing: 3px');
|
||||
expect(css).toContain('letter-spacing: 2px');
|
||||
expect(css).toContain('text-indent: 2em');
|
||||
expect(css).toContain('hyphens: auto');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -61,6 +61,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize);
|
||||
const [writingMode, setWritingMode] = useState(viewSettings.writingMode);
|
||||
const [overrideLayout, setOverrideLayout] = useState(viewSettings.overrideLayout);
|
||||
const [useBookLayout, setUseBookLayout] = useState(viewSettings.useBookLayout);
|
||||
const [doubleBorder, setDoubleBorder] = useState(viewSettings.doubleBorder);
|
||||
const [borderColor, setBorderColor] = useState(viewSettings.borderColor);
|
||||
const [showHeader, setShowHeader] = useState(viewSettings.showHeader);
|
||||
@@ -106,6 +107,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
maxInlineSize: setMaxInlineSize,
|
||||
maxBlockSize: setMaxBlockSize,
|
||||
overrideLayout: setOverrideLayout,
|
||||
useBookLayout: setUseBookLayout,
|
||||
doubleBorder: setDoubleBorder,
|
||||
borderColor: setBorderColor,
|
||||
showHeader: setShowHeader,
|
||||
@@ -315,6 +317,12 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [overrideLayout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (useBookLayout === viewSettings.useBookLayout) return;
|
||||
saveViewSettings(envConfig, bookKey, 'useBookLayout', useBookLayout);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [useBookLayout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (doubleBorder === viewSettings.doubleBorder) return;
|
||||
saveViewSettings(envConfig, bookKey, 'doubleBorder', doubleBorder, false, false);
|
||||
@@ -527,10 +535,20 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
<h2 className='mb-2 font-medium'>{_('Paragraph')}</h2>
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<div className='config-item' data-setting-id='settings.layout.useBookLayout'>
|
||||
<span className=''>{_('Use Book Layout')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={useBookLayout}
|
||||
onChange={() => setUseBookLayout(!useBookLayout)}
|
||||
/>
|
||||
</div>
|
||||
<NumberInput
|
||||
label={_('Paragraph Margin')}
|
||||
value={paragraphMargin}
|
||||
onChange={setParagraphMargin}
|
||||
disabled={useBookLayout}
|
||||
min={0}
|
||||
max={4}
|
||||
step={0.1}
|
||||
@@ -540,6 +558,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
label={_('Line Spacing')}
|
||||
value={lineHeight}
|
||||
onChange={setLineHeight}
|
||||
disabled={useBookLayout}
|
||||
min={1.0}
|
||||
max={3.0}
|
||||
step={0.1}
|
||||
@@ -550,6 +569,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
label={_('Word Spacing')}
|
||||
value={wordSpacing}
|
||||
onChange={setWordSpacing}
|
||||
disabled={useBookLayout}
|
||||
min={-4}
|
||||
max={8}
|
||||
step={0.5}
|
||||
@@ -560,6 +580,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
label={_('Letter Spacing')}
|
||||
value={letterSpacing}
|
||||
onChange={setLetterSpacing}
|
||||
disabled={useBookLayout}
|
||||
min={-2}
|
||||
max={4}
|
||||
step={0.5}
|
||||
@@ -569,6 +590,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
label={_('Text Indent')}
|
||||
value={textIndent}
|
||||
onChange={setTextIndent}
|
||||
disabled={useBookLayout}
|
||||
min={-2}
|
||||
max={4}
|
||||
step={1}
|
||||
@@ -580,6 +602,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={fullJustification}
|
||||
disabled={useBookLayout}
|
||||
onChange={() => setFullJustification(!fullJustification)}
|
||||
/>
|
||||
</div>
|
||||
@@ -589,6 +612,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={hyphenation}
|
||||
disabled={useBookLayout}
|
||||
onChange={() => setHyphenation(!hyphenation)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -235,6 +235,12 @@ const layoutPanelItems = [
|
||||
keywords: ['border', 'frame', 'vertical', 'mode'],
|
||||
section: 'Layout',
|
||||
},
|
||||
{
|
||||
id: 'settings.layout.useBookLayout',
|
||||
labelKey: _('Use Book Layout'),
|
||||
keywords: ['paragraph', 'book', 'layout', 'default', 'original', 'preserve'],
|
||||
section: 'Paragraph',
|
||||
},
|
||||
{
|
||||
id: 'settings.layout.paragraphMargin',
|
||||
labelKey: _('Paragraph Margin'),
|
||||
|
||||
@@ -221,9 +221,6 @@ export const DEFAULT_BOOK_STYLE: BookStyle = {
|
||||
fullJustification: true,
|
||||
hyphenation: true,
|
||||
theme: 'light',
|
||||
overrideFont: false,
|
||||
overrideLayout: false,
|
||||
overrideColor: false,
|
||||
backgroundTextureId: 'none',
|
||||
backgroundOpacity: 0.6,
|
||||
backgroundSize: 'cover',
|
||||
@@ -233,6 +230,11 @@ export const DEFAULT_BOOK_STYLE: BookStyle = {
|
||||
userStylesheet: '',
|
||||
userUIStylesheet: '',
|
||||
|
||||
overrideFont: false,
|
||||
overrideLayout: false,
|
||||
overrideColor: false,
|
||||
useBookLayout: false,
|
||||
|
||||
zoomMode: 'fit-page',
|
||||
spreadMode: 'auto',
|
||||
keepCoverSpread: true,
|
||||
|
||||
@@ -185,9 +185,6 @@ export interface BookStyle {
|
||||
fullJustification: boolean;
|
||||
hyphenation: boolean;
|
||||
theme: string;
|
||||
overrideFont: boolean;
|
||||
overrideLayout: boolean;
|
||||
overrideColor: boolean;
|
||||
backgroundTextureId: string;
|
||||
backgroundOpacity: number;
|
||||
backgroundSize: string;
|
||||
@@ -197,6 +194,11 @@ export interface BookStyle {
|
||||
userStylesheet: string;
|
||||
userUIStylesheet: string;
|
||||
|
||||
overrideFont: boolean;
|
||||
overrideLayout: boolean;
|
||||
overrideColor: boolean;
|
||||
useBookLayout: boolean;
|
||||
|
||||
// fixed-layout specific
|
||||
zoomMode: 'fit-page' | 'fit-width' | 'original-size' | 'custom';
|
||||
spreadMode: 'auto' | 'none';
|
||||
|
||||
+132
-119
@@ -238,46 +238,24 @@ const getColorStyles = (
|
||||
return colorStyles;
|
||||
};
|
||||
|
||||
const getLayoutStyles = (
|
||||
overrideLayout: boolean,
|
||||
const getPageLayoutStyles = (
|
||||
marginTop: number,
|
||||
marginRight: number,
|
||||
marginBottom: number,
|
||||
marginLeft: number,
|
||||
paragraphMargin: number,
|
||||
lineSpacing: number,
|
||||
wordSpacing: number,
|
||||
letterSpacing: number,
|
||||
textIndent: number,
|
||||
justify: boolean,
|
||||
hyphenate: boolean,
|
||||
zoomLevel: number,
|
||||
writingMode: string,
|
||||
vertical: boolean,
|
||||
) => {
|
||||
const layoutStyle = `
|
||||
) => `
|
||||
@namespace epub "http://www.idpf.org/2007/ops";
|
||||
html {
|
||||
--default-text-align: ${justify ? 'justify' : 'start'};
|
||||
--margin-top: ${marginTop}px;
|
||||
--margin-right: ${marginRight}px;
|
||||
--margin-bottom: ${marginBottom}px;
|
||||
--margin-left: ${marginLeft}px;
|
||||
hanging-punctuation: allow-end last;
|
||||
orphans: 2;
|
||||
widows: 2;
|
||||
}
|
||||
[align="left"] { text-align: left; }
|
||||
[align="right"] { text-align: right; }
|
||||
[align="center"] { text-align: center; }
|
||||
[align="justify"] { text-align: justify; }
|
||||
:is(hgroup, header) p {
|
||||
text-align: unset;
|
||||
hyphens: unset;
|
||||
}
|
||||
html, body {
|
||||
${writingMode === 'auto' ? '' : `writing-mode: ${writingMode} !important;`}
|
||||
text-align: var(--default-text-align);
|
||||
max-height: unset;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: text;
|
||||
@@ -306,6 +284,116 @@ const getLayoutStyles = (
|
||||
position: absolute;
|
||||
inset: -10px;
|
||||
}
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.epubtype-footnote,
|
||||
aside[epub|type~="endnote"],
|
||||
aside[epub|type~="footnote"],
|
||||
aside[epub|type~="note"],
|
||||
aside[epub|type~="rearnote"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Now begins really dirty hacks to fix some badly designed epubs */
|
||||
body {
|
||||
line-height: unset;
|
||||
}
|
||||
|
||||
.duokan-footnote-content,
|
||||
.duokan-footnote-item {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div:has(> img, > svg) {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
body.paginated-mode td:has(img), body.paginated-mode td :has(img) {
|
||||
max-height: calc(var(--available-height) * 0.8 * 1px);
|
||||
}
|
||||
|
||||
/* some epubs set insane inline-block for p */
|
||||
p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* inline images without dimension */
|
||||
.ie6 img {
|
||||
width: unset;
|
||||
height: unset;
|
||||
}
|
||||
sup img {
|
||||
height: 1em;
|
||||
}
|
||||
img.has-text-siblings {
|
||||
${vertical ? 'width: 1em;' : 'height: 1em;'}
|
||||
vertical-align: baseline;
|
||||
}
|
||||
:is(div) > img.has-text-siblings[style*="object-fit"] {
|
||||
display: block;
|
||||
height: auto;
|
||||
vertical-align: unset;
|
||||
}
|
||||
.duokan-footnote img:not([class]) {
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
div:has(img.singlepage) {
|
||||
position: relative;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* page break */
|
||||
body.paginated-mode div[style*="page-break-after: always"],
|
||||
body.paginated-mode div[style*="page-break-after:always"],
|
||||
body.paginated-mode p[style*="page-break-after: always"],
|
||||
body.paginated-mode p[style*="page-break-after:always"] {
|
||||
margin-bottom: calc(var(--available-height) * 1px);
|
||||
}
|
||||
|
||||
.br {
|
||||
display: flow-root;
|
||||
}
|
||||
|
||||
.h5_mainbody {
|
||||
overflow: unset !important;
|
||||
}
|
||||
`;
|
||||
|
||||
// Paragraph-scoped CSS controlled by the Paragraph section of the Layout
|
||||
// panel. Gated by BookStyle.useBookLayout: when true, this chunk is skipped
|
||||
// and the book's own paragraph formatting takes over.
|
||||
const getParagraphLayoutStyles = (
|
||||
overrideLayout: boolean,
|
||||
paragraphMargin: number,
|
||||
lineSpacing: number,
|
||||
wordSpacing: number,
|
||||
letterSpacing: number,
|
||||
textIndent: number,
|
||||
justify: boolean,
|
||||
hyphenate: boolean,
|
||||
vertical: boolean,
|
||||
) => `
|
||||
html {
|
||||
--default-text-align: ${justify ? 'justify' : 'start'};
|
||||
hanging-punctuation: allow-end last;
|
||||
orphans: 2;
|
||||
widows: 2;
|
||||
}
|
||||
html, body {
|
||||
text-align: var(--default-text-align);
|
||||
}
|
||||
[align="left"] { text-align: left; }
|
||||
[align="right"] { text-align: right; }
|
||||
[align="center"] { text-align: center; }
|
||||
[align="justify"] { text-align: justify; }
|
||||
:is(hgroup, header) p {
|
||||
text-align: unset;
|
||||
hyphens: unset;
|
||||
}
|
||||
p, blockquote, dd, div:not(:has(*:not(b, a, em, i, strong, u, span))) {
|
||||
line-height: ${lineSpacing} ${overrideLayout ? '!important' : ''};
|
||||
word-spacing: ${wordSpacing}px ${overrideLayout ? '!important' : ''};
|
||||
@@ -374,22 +462,11 @@ const getLayoutStyles = (
|
||||
orphans: 1;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.epubtype-footnote,
|
||||
aside[epub|type~="endnote"],
|
||||
aside[epub|type~="footnote"],
|
||||
aside[epub|type~="note"],
|
||||
aside[epub|type~="rearnote"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Now begins really dirty hacks to fix some badly designed epubs */
|
||||
body {
|
||||
line-height: unset;
|
||||
}
|
||||
/* workaround for some badly designed epubs */
|
||||
div.left *, p.left * { text-align: left; }
|
||||
div.right *, p.right * { text-align: right; }
|
||||
div.center *, p.center * { text-align: center; }
|
||||
div.justify *, p.justify * { text-align: justify; }
|
||||
|
||||
img.pi {
|
||||
${vertical ? 'transform: rotate(90deg);' : ''}
|
||||
@@ -399,79 +476,10 @@ const getLayoutStyles = (
|
||||
${vertical ? `vertical-align: unset;` : ''}
|
||||
}
|
||||
|
||||
.duokan-footnote-content,
|
||||
.duokan-footnote-item {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div:has(> img, > svg) {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
body.paginated-mode td:has(img), body.paginated-mode td :has(img) {
|
||||
max-height: calc(var(--available-height) * 0.8 * 1px);
|
||||
}
|
||||
|
||||
/* some epubs set insane inline-block for p */
|
||||
p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* inline images without dimension */
|
||||
.ie6 img {
|
||||
width: unset;
|
||||
height: unset;
|
||||
}
|
||||
sup img {
|
||||
height: 1em;
|
||||
}
|
||||
img.has-text-siblings {
|
||||
${vertical ? 'width: 1em;' : 'height: 1em;'}
|
||||
vertical-align: baseline;
|
||||
}
|
||||
:is(div) > img.has-text-siblings[style*="object-fit"] {
|
||||
display: block;
|
||||
height: auto;
|
||||
vertical-align: unset;
|
||||
}
|
||||
.duokan-footnote img:not([class]) {
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
div:has(img.singlepage) {
|
||||
position: relative;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* page break */
|
||||
body.paginated-mode div[style*="page-break-after: always"],
|
||||
body.paginated-mode div[style*="page-break-after:always"],
|
||||
body.paginated-mode p[style*="page-break-after: always"],
|
||||
body.paginated-mode p[style*="page-break-after:always"] {
|
||||
margin-bottom: calc(var(--available-height) * 1px);
|
||||
}
|
||||
|
||||
/* workaround for some badly designed epubs */
|
||||
div.left *, p.left * { text-align: left; }
|
||||
div.right *, p.right * { text-align: right; }
|
||||
div.center *, p.center * { text-align: center; }
|
||||
div.justify *, p.justify * { text-align: justify; }
|
||||
|
||||
.br {
|
||||
display: flow-root;
|
||||
}
|
||||
|
||||
.h5_mainbody {
|
||||
overflow: unset !important;
|
||||
}
|
||||
|
||||
.nonindent, .noindent {
|
||||
text-indent: unset !important;
|
||||
}
|
||||
`;
|
||||
return layoutStyle;
|
||||
};
|
||||
|
||||
export const getFootnoteStyles = () => `
|
||||
.duokan-footnote-content,
|
||||
@@ -587,23 +595,28 @@ export const getStyles = (viewSettings: ViewSettings, themeCode?: ThemeCode) =>
|
||||
if (!themeCode) {
|
||||
themeCode = getThemeCode();
|
||||
}
|
||||
const layoutStyles = getLayoutStyles(
|
||||
viewSettings.overrideLayout!,
|
||||
const pageLayoutStyles = getPageLayoutStyles(
|
||||
viewSettings.marginTopPx,
|
||||
viewSettings.marginRightPx,
|
||||
viewSettings.marginBottomPx,
|
||||
viewSettings.marginLeftPx,
|
||||
viewSettings.paragraphMargin!,
|
||||
viewSettings.lineHeight!,
|
||||
viewSettings.wordSpacing!,
|
||||
viewSettings.letterSpacing!,
|
||||
viewSettings.textIndent!,
|
||||
viewSettings.fullJustification!,
|
||||
viewSettings.hyphenation!,
|
||||
1.0,
|
||||
viewSettings.writingMode!,
|
||||
viewSettings.vertical!,
|
||||
);
|
||||
const paragraphLayoutStyles = viewSettings.useBookLayout
|
||||
? ''
|
||||
: getParagraphLayoutStyles(
|
||||
viewSettings.overrideLayout!,
|
||||
viewSettings.paragraphMargin!,
|
||||
viewSettings.lineHeight!,
|
||||
viewSettings.wordSpacing!,
|
||||
viewSettings.letterSpacing!,
|
||||
viewSettings.textIndent!,
|
||||
viewSettings.fullJustification!,
|
||||
viewSettings.hyphenation!,
|
||||
viewSettings.vertical!,
|
||||
);
|
||||
// scale the font size on-the-fly so that we can sync the same font size on different devices
|
||||
const isMobile = ['ios', 'android'].includes(getOSPlatform());
|
||||
const fontScale = isMobile ? 1.25 : 1;
|
||||
@@ -629,7 +642,7 @@ export const getStyles = (viewSettings: ViewSettings, themeCode?: ThemeCode) =>
|
||||
);
|
||||
const translationStyles = getTranslationStyles(viewSettings.showTranslateSource!);
|
||||
const userStylesheet = viewSettings.userStylesheet!;
|
||||
return `${layoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${userStylesheet}`;
|
||||
return `${pageLayoutStyles}\n${paragraphLayoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${userStylesheet}`;
|
||||
};
|
||||
|
||||
export const applyTranslationStyle = (viewSettings: ViewSettings) => {
|
||||
|
||||
Reference in New Issue
Block a user