feat: add options to reset settings in the config dialog, closes #1111 (#1475)

This commit is contained in:
Huang Xin
2025-06-26 15:59:34 +08:00
committed by GitHub
parent 460d37c656
commit bc2c3a2c9a
34 changed files with 372 additions and 92 deletions
@@ -381,5 +381,7 @@
"Enter CSS for reader interface styling...": "أدخل CSS لتنسيق واجهة القارئ...",
"Crop": "قص",
"Book Covers": "أغلفة الكتب",
"Fit": "ملاءمة"
"Fit": "ملاءمة",
"Reset {{settings}}": "إعادة تعيين {{settings}}",
"Reset Settings": "إعادة تعيين الإعدادات"
}
@@ -369,5 +369,7 @@
"Enter CSS for reader interface styling...": "CSS für die Leseroberfläche eingeben...",
"Crop": "Zuschneiden",
"Book Covers": "Buchcover",
"Fit": "Anpassen"
"Fit": "Anpassen",
"Reset {{settings}}": "{{settings}} zurücksetzen",
"Reset Settings": "Einstellungen zurücksetzen"
}
@@ -369,5 +369,7 @@
"Enter CSS for reader interface styling...": "Εισαγάγετε CSS για στυλ διεπαφής αναγνώστη...",
"Crop": "Περικοπή",
"Book Covers": "Εξώφυλλα βιβλίων",
"Fit": "Προσαρμογή"
"Fit": "Προσαρμογή",
"Reset {{settings}}": "Επαναφορά {{settings}}",
"Reset Settings": "Επαναφορά ρυθμίσεων"
}
@@ -372,5 +372,7 @@
"Enter CSS for reader interface styling...": "Introduce CSS para la interfaz de lectura...",
"Crop": "Recortar",
"Book Covers": "Portadas de libros",
"Fit": "Encajar"
"Fit": "Encajar",
"Reset {{settings}}": "Restablecer {{settings}}",
"Reset Settings": "Restablecer configuración"
}
@@ -372,5 +372,7 @@
"Enter CSS for reader interface styling...": "Saisissez le CSS pour linterface de lecture...",
"Crop": "Rogner",
"Book Covers": "Couvertures de livres",
"Fit": "Adapter"
"Fit": "Adapter",
"Reset {{settings}}": "Réinitialiser {{settings}}",
"Reset Settings": "Réinitialiser les paramètres"
}
@@ -369,5 +369,7 @@
"Enter CSS for reader interface styling...": "रीडर इंटरफ़ेस के लिए CSS दर्ज करें...",
"Crop": "काटें",
"Book Covers": "पुस्तक कवर",
"Fit": "फिट"
"Fit": "फिट",
"Reset {{settings}}": "रीसेट {{settings}}",
"Reset Settings": "सेटिंग्स रीसेट करें"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "Masukkan CSS untuk gaya antarmuka pembaca...",
"Crop": "Potong",
"Book Covers": "Sampul Buku",
"Fit": "Pas"
"Fit": "Pas",
"Reset {{settings}}": "Atur Ulang {{settings}}",
"Reset Settings": "Atur Ulang Pengaturan"
}
@@ -372,5 +372,7 @@
"Enter CSS for reader interface styling...": "Inserisci il CSS per l'interfaccia di lettura...",
"Crop": "Ritaglia",
"Book Covers": "Copertine libri",
"Fit": "Adatta"
"Fit": "Adatta",
"Reset {{settings}}": "Reimposta {{settings}}",
"Reset Settings": "Reimposta impostazioni"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "リーダーインターフェース用CSSを入力...",
"Crop": "トリミング",
"Book Covers": "書籍の表紙",
"Fit": "フィット"
"Fit": "フィット",
"Reset {{settings}}": "{{settings}}をリセット",
"Reset Settings": "設定をリセット"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "리더 인터페이스 스타일용 CSS 입력...",
"Crop": "자르기",
"Book Covers": "책 표지",
"Fit": "맞춤"
"Fit": "맞춤",
"Reset {{settings}}": "{{settings}} 재설정",
"Reset Settings": "설정 재설정"
}
@@ -369,5 +369,7 @@
"Enter CSS for reader interface styling...": "Voer CSS in voor de opmaak van de leesinterface...",
"Crop": "Bijsnijden",
"Book Covers": "Boekomslagen",
"Fit": "Passend maken"
"Fit": "Passend maken",
"Reset {{settings}}": "Reset {{settings}}",
"Reset Settings": "Instellingen resetten"
}
@@ -375,5 +375,7 @@
"Enter CSS for reader interface styling...": "Wprowadź CSS dla stylu interfejsu czytnika...",
"Crop": "Przytnij",
"Book Covers": "Okładki książek",
"Fit": "Pasuje"
"Fit": "Pasuje",
"Reset {{settings}}": "Zresetuj {{settings}}",
"Reset Settings": "Zresetuj ustawienia"
}
@@ -372,5 +372,7 @@
"Enter CSS for reader interface styling...": "Insira o CSS para o estilo da interface de leitura...",
"Crop": "Cortar",
"Book Covers": "Capas de Livros",
"Fit": "Encaixar"
"Fit": "Encaixar",
"Reset {{settings}}": "Redefinir {{settings}}",
"Reset Settings": "Redefinir Configurações"
}
@@ -375,5 +375,7 @@
"Enter CSS for reader interface styling...": "Введите CSS для оформления интерфейса ридера...",
"Crop": "Обрезать",
"Book Covers": "Обложки книг",
"Fit": "Подогнать"
"Fit": "Подогнать",
"Reset {{settings}}": "Сбросить {{settings}}",
"Reset Settings": "Сбросить настройки"
}
@@ -369,5 +369,7 @@
"Enter CSS for reader interface styling...": "Okuyucu arayüzü stili için CSS girin...",
"Crop": "Kes",
"Book Covers": "Kitap Kapağı",
"Fit": "Uygun"
"Fit": "Uygun",
"Reset {{settings}}": "{{settings}}'i Sıfırla",
"Reset Settings": "Ayarları Sıfırla"
}
@@ -375,5 +375,7 @@
"Enter CSS for reader interface styling...": "Введіть CSS для стилізації інтерфейсу рідера...",
"Crop": "Обрізати",
"Book Covers": "Обкладинки книг",
"Fit": "Вписати"
"Fit": "Вписати",
"Reset {{settings}}": "Скинути {{settings}}",
"Reset Settings": "Скинути налаштування"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "Nhập CSS cho giao diện trình đọc...",
"Crop": "Cắt",
"Book Covers": "Bìa sách",
"Fit": "Vừa vặn"
"Fit": "Vừa vặn",
"Reset {{settings}}": "Đặt lại {{settings}}",
"Reset Settings": "Đặt lại cài đặt"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "输入阅读器界面样式的CSS…",
"Crop": "裁剪",
"Book Covers": "书籍封面",
"Fit": "适应"
"Fit": "适应",
"Reset {{settings}}": "重置{{settings}}",
"Reset Settings": "重置设置"
}
@@ -366,5 +366,7 @@
"Enter CSS for reader interface styling...": "輸入閱讀器介面樣式的CSS…",
"Crop": "裁剪",
"Book Covers": "書籍封面",
"Fit": "適應"
"Fit": "適應",
"Reset {{settings}}": "重置{{settings}}",
"Reset Settings": "重置設置"
}
@@ -18,12 +18,14 @@ import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useSettingsStore } from '@/store/settingsStore';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { CODE_LANGUAGES, CodeLanguage, manageSyntaxHighlighting } from '@/utils/highlightjs';
import { SettingsPanelPanelProp } from './SettingsDialog';
import Select from '@/components/Select';
import ThemeEditor from './ThemeEditor';
const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const ColorPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { themeMode, themeColor, isDarkMode, setThemeMode, setThemeColor, saveCustomTheme } =
useThemeStore();
@@ -44,6 +46,24 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [codeHighlighting, setcodeHighlighting] = useState(viewSettings.codeHighlighting!);
const [codeLanguage, setCodeLanguage] = useState(viewSettings.codeLanguage!);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
overrideColor: setOverrideColor,
invertImgColorInDark: setInvertImgColorInDark,
codeHighlighting: setcodeHighlighting,
codeLanguage: setCodeLanguage,
});
setThemeColor('default');
setThemeMode('auto');
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (invertImgColorInDark === viewSettings.invertImgColorInDark) return;
saveViewSettings(envConfig, bookKey, 'invertImgColorInDark', invertImgColorInDark);
@@ -8,9 +8,11 @@ import { getStyles } from '@/utils/style';
import { getMaxInlineSize } from '@/utils/config';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { RELOAD_BEFREE_SAVED_TIMEOUT_MS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import NumberInput from './NumberInput';
const ControlPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const ControlPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { envConfig, appService } = useEnv();
const { getView, getViewSettings } = useReaderStore();
@@ -28,6 +30,26 @@ const ControlPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [animated, setAnimated] = useState(viewSettings.animated!);
const [allowScript, setAllowScript] = useState(viewSettings.allowScript!);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
scrolled: setScrolledMode,
continuousScroll: setIsContinuousScroll,
scrollingOverlap: setScrollingOverlap,
volumeKeysToFlip: setVolumeKeysToFlip,
disableClick: setIsDisableClick,
swapClickArea: setSwapClickArea,
animated: setAnimated,
allowScript: setAllowScript,
});
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (isScrolledMode === viewSettings.scrolled) return;
saveViewSettings(envConfig, bookKey, 'scrolled', isScrolledMode);
@@ -3,20 +3,30 @@ import React from 'react';
import { MdCheck } from 'react-icons/md';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useDefaultIconSize } from '@/hooks/useResponsiveSize';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { SettingsPanelType } from './SettingsDialog';
import MenuItem from '@/components/MenuItem';
interface DialogMenuProps {
toggleDropdown?: () => void;
activePanel: SettingsPanelType;
setIsDropdownOpen?: (open: boolean) => void;
onReset: () => void;
resetLabel?: string;
}
const DialogMenu: React.FC<DialogMenuProps> = ({ toggleDropdown }) => {
const DialogMenu: React.FC<DialogMenuProps> = ({ setIsDropdownOpen, onReset, resetLabel }) => {
const _ = useTranslation();
const iconSize = useDefaultIconSize();
const iconSize = useResponsiveSize(16);
const { isFontLayoutSettingsGlobal, setFontLayoutSettingsGlobal } = useSettingsStore();
const handleToggleGlobal = () => {
setFontLayoutSettingsGlobal(!isFontLayoutSettingsGlobal);
toggleDropdown?.();
setIsDropdownOpen?.(false);
};
const handleResetToDefaults = () => {
onReset();
setIsDropdownOpen?.(false);
};
return (
@@ -27,24 +37,18 @@ const DialogMenu: React.FC<DialogMenuProps> = ({ toggleDropdown }) => {
'text-base sm:text-sm',
)}
>
<button
className='hover:bg-base-200 text-base-content flex w-full items-center justify-between rounded-md p-1'
<MenuItem
label={_('Global Settings')}
tooltip={isFontLayoutSettingsGlobal ? _('Apply to All Books') : _('Apply to This Book')}
buttonClass='lg:tooltip'
Icon={
isFontLayoutSettingsGlobal ? (
<MdCheck size={iconSize} className='text-base-content' />
) : null
}
onClick={handleToggleGlobal}
>
<div className='flex items-center'>
<span style={{ minWidth: `${iconSize}px` }}>
{isFontLayoutSettingsGlobal && <MdCheck className='text-base-content' />}
</span>
<div
className='lg:tooltip'
data-tip={
isFontLayoutSettingsGlobal ? _('Apply to All Books') : _('Apply to This Book')
}
>
<span className='ml-2 whitespace-nowrap'>{_('Global Settings')}</span>
</div>
</div>
</button>
/>
<MenuItem label={resetLabel || _('Reset Settings')} onClick={handleResetToDefaults} />
</div>
);
};
@@ -24,6 +24,8 @@ import { getOSPlatform, isCJKEnv } from '@/utils/misc';
import { getSysFontsList } from '@/utils/bridge';
import { isTauriAppPlatform } from '@/services/environment';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import NumberInput from './NumberInput';
import FontDropdown from './FontDropDown';
@@ -79,7 +81,7 @@ const FontFace = ({
);
};
const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const FontPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { envConfig } = useEnv();
const { getView, getViewSettings } = useReaderStore();
@@ -119,10 +121,10 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
break;
}
const [sysFonts, setSysFonts] = useState<string[]>(defaultSysFonts);
const [defaultFont, setDefaultFont] = useState(viewSettings.defaultFont!);
const [defaultFontSize, setDefaultFontSize] = useState(viewSettings.defaultFontSize!);
const [minFontSize, setMinFontSize] = useState(viewSettings.minimumFontSize!);
const [overrideFont, setOverrideFont] = useState(viewSettings.overrideFont!);
const [defaultFont, setDefaultFont] = useState(viewSettings.defaultFont!);
const [defaultCJKFont, setDefaultCJKFont] = useState(viewSettings.defaultCJKFont!);
const [serifFont, setSerifFont] = useState(viewSettings.serifFont!);
const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont!);
@@ -132,6 +134,27 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
return genCJKFontsList(sysFonts);
});
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
defaultFont: setDefaultFont,
defaultFontSize: setDefaultFontSize,
minimumFontSize: setMinFontSize,
overrideFont: setOverrideFont,
defaultCJKFont: setDefaultCJKFont,
serifFont: setSerifFont,
sansSerifFont: setSansSerifFont,
monospaceFont: setMonospaceFont,
fontWeight: setFontWeight,
});
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
setCJKFonts((prev) => {
const newFonts = genCJKFontsList(sysFonts);
@@ -8,20 +8,39 @@ import { useTranslation } from '@/hooks/useTranslation';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { getTranslators } from '@/services/translators';
import { TRANSLATED_LANGS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { initDayjs } from '@/utils/time';
import Select from '@/components/Select';
const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { token } = useAuth();
const { envConfig } = useEnv();
const { getViewSettings, setViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const [uiLanguage, setUILanguage] = useState(viewSettings.uiLanguage!);
const [translationEnabled, setTranslationEnabled] = useState(viewSettings.translationEnabled!);
const [translationProvider, setTranslationProvider] = useState(viewSettings.translationProvider!);
const [translateTargetLang, setTranslateTargetLang] = useState(viewSettings.translateTargetLang!);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
uiLanguage: setUILanguage,
translationEnabled: setTranslationEnabled,
translationProvider: setTranslationProvider,
translateTargetLang: setTranslateTargetLang,
});
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const getCurrentUILangOption = () => {
const uiLanguage = viewSettings.uiLanguage;
return {
@@ -43,10 +62,7 @@ const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const handleSelectUILang = (event: React.ChangeEvent<HTMLSelectElement>) => {
const option = event.target.value;
saveViewSettings(envConfig, bookKey, 'uiLanguage', option, false, false);
const locale = option ? option : navigator.language;
i18n.changeLanguage(locale);
initDayjs(locale);
setUILanguage(option);
};
const getTranslationProviderOptions = () => {
@@ -97,6 +113,15 @@ const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
setViewSettings(bookKey, { ...viewSettings });
};
useEffect(() => {
if (uiLanguage === viewSettings.uiLanguage) return;
saveViewSettings(envConfig, bookKey, 'uiLanguage', uiLanguage, false, false);
const locale = uiLanguage ? uiLanguage : navigator.language;
i18n.changeLanguage(locale);
initDayjs(locale);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [uiLanguage]);
useEffect(() => {
if (translationEnabled === viewSettings.translationEnabled) return;
saveViewSettings(envConfig, bookKey, 'translationEnabled', translationEnabled, true, false);
@@ -8,6 +8,7 @@ import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { isCJKEnv } from '@/utils/misc';
import { getStyles } from '@/utils/style';
import { getMaxInlineSize } from '@/utils/config';
@@ -15,9 +16,10 @@ import { lockScreenOrientation } from '@/utils/bridge';
import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book';
import { MIGHT_BE_RTL_LANGS, RELOAD_BEFREE_SAVED_TIMEOUT_MS } from '@/services/constants';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { SettingsPanelPanelProp } from './SettingsDialog';
import NumberInput from './NumberInput';
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { envConfig, appService } = useEnv();
const { getView, getViewSettings, getGridInsets, setViewSettings } = useReaderStore();
@@ -63,9 +65,47 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll!);
const [showRemainingTime, setShowRemainingTime] = useState(viewSettings.showRemainingTime!);
const [showPageNumber, setShowPageNumber] = useState(viewSettings.showPageNumber!);
const [screenOrientation, setScreenOrientation] = useState(viewSettings.screenOrientation!);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
paragraphMargin: setParagraphMargin,
lineHeight: setLineHeight,
wordSpacing: setWordSpacing,
letterSpacing: setLetterSpacing,
textIndent: setTextIndent,
fullJustification: setFullJustification,
hyphenation: setHyphenation,
marginTopPx: setMarginTopPx,
marginBottomPx: setMarginBottomPx,
marginLeftPx: setMarginLeftPx,
marginRightPx: setMarginRightPx,
compactMarginTopPx: setCompactMarginTopPx,
compactMarginBottomPx: setCompactMarginBottomPx,
compactMarginLeftPx: setCompactMarginLeftPx,
compactMarginRightPx: setCompactMarginRightPx,
gapPercent: setGapPercent,
maxColumnCount: setMaxColumnCount,
maxInlineSize: setMaxInlineSize,
maxBlockSize: setMaxBlockSize,
overrideLayout: setOverrideLayout,
doubleBorder: setDoubleBorder,
borderColor: setBorderColor,
showHeader: setShowHeader,
showFooter: setShowFooter,
showBarsOnScroll: setShowBarsOnScroll,
showRemainingTime: setShowRemainingTime,
showPageNumber: setShowPageNumber,
});
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'paragraphMargin', paragraphMargin);
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -1,17 +1,19 @@
import clsx from 'clsx';
import cssbeautify from 'cssbeautify';
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { getStyles } from '@/utils/style';
import cssValidate from '@/utils/css';
type CSSType = 'book' | 'reader';
const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const MiscPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { appService } = useEnv();
const { settings, isFontLayoutSettingsGlobal, setSettings } = useSettingsStore();
@@ -29,6 +31,22 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const contentTextareaRef = useRef<HTMLTextAreaElement | null>(null);
const uiTextareaRef = useRef<HTMLTextAreaElement | null>(null);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
userStylesheet: setDraftContentStylesheet,
userUIStylesheet: setDraftUIStylesheet,
});
applyStyles('book', true);
applyStyles('reader', true);
};
useEffect(() => {
onRegisterReset(handleReset);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const validateCSS = (cssInput: string): { isValid: boolean; error?: string } => {
if (!cssInput.trim()) return { isValid: true };
@@ -64,9 +82,9 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
}
};
const applyStyles = (type: CSSType) => {
const applyStyles = (type: CSSType, clear = false) => {
const cssInput = type === 'book' ? draftContentStylesheet : draftUIStylesheet;
const formattedCSS = cssbeautify(cssInput, {
const formattedCSS = cssbeautify(clear ? '' : cssInput, {
indent: ' ',
openbrace: 'end-of-line',
autosemicolon: true,
@@ -22,7 +22,11 @@ import ControlPanel from './ControlPanel';
import LangPanel from './LangPanel';
import MiscPanel from './MiscPanel';
type SettingsPanelType = 'Font' | 'Layout' | 'Color' | 'Control' | 'Language' | 'Custom';
export type SettingsPanelType = 'Font' | 'Layout' | 'Color' | 'Control' | 'Language' | 'Custom';
export type SettingsPanelPanelProp = {
bookKey: string;
onRegisterReset: (resetFn: () => void) => void;
};
type TabConfig = {
tab: SettingsPanelType;
@@ -84,6 +88,28 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
localStorage.setItem('lastConfigPanel', tab);
};
const [resetFunctions, setResetFunctions] = useState<
Record<SettingsPanelType, (() => void) | null>
>({
Font: null,
Layout: null,
Color: null,
Control: null,
Language: null,
Custom: null,
});
const registerResetFunction = (panel: SettingsPanelType, resetFn: () => void) => {
setResetFunctions((prev) => ({ ...prev, [panel]: resetFn }));
};
const handleResetCurrentPanel = () => {
const resetFn = resetFunctions[activePanel];
if (resetFn) {
resetFn();
}
};
const handleClose = () => {
setFontLayoutSettingsDialogOpen(false);
};
@@ -130,6 +156,8 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
};
}, []);
const currentPanel = tabConfig.find((tab) => tab.tab === activePanel);
return (
<Dialog
isOpen={true}
@@ -140,7 +168,7 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
header={
<div className='flex w-full flex-col items-center'>
<div className='tab-title flex pb-2 text-base font-semibold sm:hidden'>
{tabConfig.find((tab) => tab.tab === activePanel)?.label || ''}
{currentPanel?.label || ''}
</div>
<div className='flex w-full flex-row items-center justify-between'>
<button
@@ -184,7 +212,15 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0 flex items-center justify-center'
toggleButton={<PiDotsThreeVerticalBold />}
>
<DialogMenu />
<DialogMenu
activePanel={activePanel}
onReset={handleResetCurrentPanel}
resetLabel={
currentPanel
? _('Reset {{settings}}', { settings: currentPanel.label })
: undefined
}
/>
</Dropdown>
<button
onClick={handleClose}
@@ -209,12 +245,39 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
</div>
}
>
{activePanel === 'Font' && <FontPanel bookKey={bookKey} />}
{activePanel === 'Layout' && <LayoutPanel bookKey={bookKey} />}
{activePanel === 'Color' && <ColorPanel bookKey={bookKey} />}
{activePanel === 'Control' && <ControlPanel bookKey={bookKey} />}
{activePanel === 'Language' && <LangPanel bookKey={bookKey} />}
{activePanel === 'Custom' && <MiscPanel bookKey={bookKey} />}
{activePanel === 'Font' && (
<FontPanel bookKey={bookKey} onRegisterReset={(fn) => registerResetFunction('Font', fn)} />
)}
{activePanel === 'Layout' && (
<LayoutPanel
bookKey={bookKey}
onRegisterReset={(fn) => registerResetFunction('Layout', fn)}
/>
)}
{activePanel === 'Color' && (
<ColorPanel
bookKey={bookKey}
onRegisterReset={(fn) => registerResetFunction('Color', fn)}
/>
)}
{activePanel === 'Control' && (
<ControlPanel
bookKey={bookKey}
onRegisterReset={(fn) => registerResetFunction('Control', fn)}
/>
)}
{activePanel === 'Language' && (
<LangPanel
bookKey={bookKey}
onRegisterReset={(fn) => registerResetFunction('Language', fn)}
/>
)}
{activePanel === 'Custom' && (
<MiscPanel
bookKey={bookKey}
onRegisterReset={(fn) => registerResetFunction('Custom', fn)}
/>
)}
</Dialog>
);
};
@@ -1,8 +1,6 @@
import { useReaderStore } from '@/store/readerStore';
import { useNotebookStore } from '@/store/notebookStore';
import { isTauriAppPlatform } from '@/services/environment';
import useShortcuts from '@/hooks/useShortcuts';
import useBooksManager from './useBooksManager';
import { useSidebarStore } from '@/store/sidebarStore';
import { useSettingsStore } from '@/store/settingsStore';
import { getStyles } from '@/utils/style';
@@ -10,6 +8,8 @@ import { tauriHandleToggleFullScreen, tauriQuitApp } from '@/utils/window';
import { eventDispatcher } from '@/utils/event';
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ZOOM_STEP } from '@/services/constants';
import { viewPagination } from './usePagination';
import useShortcuts from '@/hooks/useShortcuts';
import useBooksManager from './useBooksManager';
interface UseBookShortcutsProps {
sideBarBookKey: string | null;
@@ -0,0 +1,28 @@
import { Dispatch, SetStateAction } from 'react';
import { useEnv } from '@/context/EnvContext';
import { ViewSettings } from '@/types/book';
type SetterKey = keyof ViewSettings;
type SetterValue = SetStateAction<string> & SetStateAction<number> & SetStateAction<boolean>;
type StateSetters = Partial<{
[Key in SetterKey]: Dispatch<SetterValue>;
}>;
export const useResetViewSettings = () => {
const { appService } = useEnv();
const resetToDefaults = (setters: StateSetters) => {
if (!appService) return;
const defaultSettings = appService.getDefaultViewSettings();
Object.entries(setters).forEach(([settingKey, setter]) => {
const freshValue = defaultSettings[settingKey as SetterKey];
if (freshValue !== undefined) {
setter(freshValue as SetterValue);
}
});
};
return resetToDefaults;
};
+1 -1
View File
@@ -36,7 +36,7 @@ const Dropdown: React.FC<DropdownProps> = ({
: children;
return (
<div className='dropdown-container'>
<div className='dropdown-container flex'>
{isOpen && (
<div className='fixed inset-0 bg-transparent' onClick={() => setIsDropdownOpen(false)} />
)}
@@ -6,6 +6,7 @@ import { useResponsiveSize } from '@/hooks/useResponsiveSize';
interface MenuItemProps {
label: string;
description?: string;
tooltip?: string;
buttonClass?: string;
labelClass?: string;
shortcut?: string;
@@ -19,6 +20,7 @@ interface MenuItemProps {
const MenuItem: React.FC<MenuItemProps> = ({
label,
description,
tooltip,
buttonClass,
labelClass,
shortcut,
@@ -36,6 +38,7 @@ const MenuItem: React.FC<MenuItemProps> = ({
disabled && 'btn-disabled text-gray-400',
buttonClass,
)}
data-tip={tooltip ? tooltip : ''}
onClick={onClick}
disabled={disabled}
>
+17 -21
View File
@@ -2,7 +2,7 @@ import { AppPlatform, AppService, OsPlatform } from '@/types/system';
import { SystemSettings } from '@/types/settings';
import { FileSystem, BaseDir } from '@/types/system';
import { Book, BookConfig, BookContent, BookFormat } from '@/types/book';
import { Book, BookConfig, BookContent, BookFormat, ViewSettings } from '@/types/book';
import {
getDir,
getLocalBookFilename,
@@ -74,6 +74,20 @@ export abstract class BaseAppService implements AppService {
abstract selectDirectory(): Promise<string>;
abstract selectFiles(name: string, extensions: string[]): Promise<string[]>;
getDefaultViewSettings(): ViewSettings {
return {
...DEFAULT_BOOK_LAYOUT,
...DEFAULT_BOOK_STYLE,
...DEFAULT_BOOK_FONT,
...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}),
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
...DEFAULT_VIEW_CONFIG,
...DEFAULT_TTS_CONFIG,
...DEFAULT_SCREEN_CONFIG,
...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() },
};
}
async loadSettings(): Promise<SystemSettings> {
let settings: SystemSettings;
const { fp, base } = this.resolvePath('settings.json', 'Settings');
@@ -90,15 +104,7 @@ export abstract class BaseAppService implements AppService {
settings = { ...DEFAULT_SYSTEM_SETTINGS, ...settings };
settings.globalReadSettings = { ...DEFAULT_READSETTINGS, ...settings.globalReadSettings };
settings.globalViewSettings = {
...DEFAULT_BOOK_LAYOUT,
...DEFAULT_BOOK_STYLE,
...DEFAULT_BOOK_FONT,
...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}),
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
...DEFAULT_VIEW_CONFIG,
...DEFAULT_TTS_CONFIG,
...DEFAULT_SCREEN_CONFIG,
...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() },
...this.getDefaultViewSettings(),
...settings.globalViewSettings,
};
} catch {
@@ -110,17 +116,7 @@ export abstract class BaseAppService implements AppService {
...DEFAULT_READSETTINGS,
...(this.isMobile ? DEFAULT_MOBILE_READSETTINGS : {}),
},
globalViewSettings: {
...DEFAULT_BOOK_LAYOUT,
...DEFAULT_BOOK_STYLE,
...DEFAULT_BOOK_FONT,
...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}),
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
...DEFAULT_VIEW_CONFIG,
...DEFAULT_TTS_CONFIG,
...DEFAULT_SCREEN_CONFIG,
...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() },
},
globalViewSettings: this.getDefaultViewSettings(),
} as SystemSettings;
await this.fs.createDir('', 'Books', true);
+1 -2
View File
@@ -2,7 +2,6 @@ export type BookFormat = 'EPUB' | 'PDF' | 'MOBI' | 'CBZ' | 'FB2' | 'FBZ';
export type BookNoteType = 'bookmark' | 'annotation' | 'excerpt';
export type HighlightStyle = 'highlight' | 'underline' | 'squiggly';
export type HighlightColor = 'red' | 'yellow' | 'green' | 'blue' | 'violet';
import { CodeLanguage } from '@/utils/highlightjs';
export interface Book {
// if Book is a remote book we just lazy load the book content via url
@@ -115,7 +114,7 @@ export interface BookStyle {
overrideLayout: boolean;
overrideColor: boolean;
codeHighlighting: boolean;
codeLanguage: CodeLanguage;
codeLanguage: string;
userStylesheet: string;
userUIStylesheet: string;
}
+2 -1
View File
@@ -1,5 +1,5 @@
import { SystemSettings } from './settings';
import { Book, BookConfig, BookContent } from './book';
import { Book, BookConfig, BookContent, ViewSettings } from './book';
import { BookDoc } from '@/libs/document';
import { ProgressHandler } from '@/utils/transfer';
@@ -44,6 +44,7 @@ export interface AppService {
selectDirectory(): Promise<string>;
selectFiles(name: string, extensions: string[]): Promise<string[]>;
getDefaultViewSettings(): ViewSettings;
loadSettings(): Promise<SystemSettings>;
saveSettings(settings: SystemSettings): Promise<void>;
importBook(