feat: support styled reader UI via custom CSS, closes #240 (#503)

This commit is contained in:
Huang Xin
2025-03-06 20:56:43 +08:00
committed by GitHub
parent 77bba7bd0c
commit 0f44fff7cd
4 changed files with 30 additions and 3 deletions
@@ -12,6 +12,7 @@ import { useAutoHideScrollbar } from '../hooks/useAutoHideScrollbar';
import { getStyles, mountAdditionalFonts } from '@/utils/style';
import { getBookDirFromLanguage, getBookDirFromWritingMode } from '@/utils/book';
import { useTheme } from '@/hooks/useTheme';
import { useUICSS } from '@/hooks/useUICSS';
import {
handleKeydown,
handleMousedown,
@@ -36,6 +37,7 @@ const FoliateViewer: React.FC<{
const { getViewSettings, setViewSettings } = useReaderStore();
const { getParallels } = useParallelViewStore();
const { themeCode } = useTheme();
const viewSettings = getViewSettings(bookKey)!;
const [toastMessage, setToastMessage] = useState('');
useEffect(() => {
@@ -43,6 +45,7 @@ const FoliateViewer: React.FC<{
return () => clearTimeout(timer);
}, [toastMessage]);
useUICSS(viewSettings);
useProgressSync(bookKey);
useProgressAutoSave(bookKey);
@@ -138,7 +141,6 @@ const FoliateViewer: React.FC<{
document.body.append(view);
containerRef.current?.appendChild(view);
const viewSettings = getViewSettings(bookKey)!;
const writingMode = viewSettings.writingMode;
if (writingMode) {
const settingsDir = getBookDirFromWritingMode(writingMode);
@@ -52,7 +52,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
setDraftStylesheet(formattedCSS);
setDraftStylesheetSaved(true);
viewSettings.userStylesheet = formattedCSS;
setViewSettings(bookKey, viewSettings);
setViewSettings(bookKey, { ...viewSettings });
if (isFontLayoutSettingsGlobal) {
settings.globalViewSettings.userStylesheet = formattedCSS;
+25
View File
@@ -0,0 +1,25 @@
import { ViewSettings } from '@/types/book';
import { useEffect, useState } from 'react';
// This hook allows you to inject custom CSS into the reader UI.
// Note that the book content is rendered in an iframe, so UI CSS won't affect book rendering.
export const useUICSS = (viewSettings: ViewSettings) => {
const [styleElement, setStyleElement] = useState<HTMLStyleElement | null>(null);
useEffect(() => {
if (!viewSettings) return;
if (styleElement) {
styleElement.remove();
}
const newStyleEl = document.createElement('style');
newStyleEl.textContent = viewSettings.userStylesheet;
document.head.appendChild(newStyleEl);
setStyleElement(newStyleEl);
return () => {
newStyleEl.remove();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [viewSettings]);
};