feat(rsvp): apply reader font face/family settings to the RSVP word (#4519) (#4537)

RSVP displayed the focal word in a hardcoded monospace font, ignoring the
reader's configured font. Resolve the reader's body font-family (serif or
sans-serif chain, per the "Default Font" setting, including the chosen
typeface, CJK font, and any user-imported custom font) and apply it to the
RSVP word display.

Custom and additional fonts are already mounted in the top document where
the overlay renders, so the resolved family resolves the same typeface. The
monospace fallback is kept only when no font setting is available.

Extracts the font-family list building from getFontStyles into a shared
buildFontFamilyLists helper and exposes getBaseFontFamily for top-level UI.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-12 01:24:46 +08:00
committed by GitHub
parent 6dc42222e0
commit cf41e7d50d
6 changed files with 149 additions and 13 deletions
@@ -13,6 +13,7 @@ import {
buildRsvpExitConfigUpdate,
} from '@/services/rsvp';
import { eventDispatcher } from '@/utils/event';
import { getBaseFontFamily } from '@/utils/style';
import { useEnv } from '@/context/EnvContext';
import { useTranslation } from '@/hooks/useTranslation';
import { BookNote, PageInfo } from '@/types/book';
@@ -114,7 +115,7 @@ const RSVPControl: React.FC<RSVPControlProps> = ({ bookKey, gridInsets }) => {
const _ = useTranslation();
const { envConfig } = useEnv();
const { settings } = useSettingsStore();
const { getView, getProgress } = useReaderStore();
const { getView, getProgress, getViewSettings } = useReaderStore();
const { getBookData, getConfig, setConfig, saveConfig } = useBookDataStore();
const { themeCode } = useThemeStore();
@@ -521,6 +522,12 @@ const RSVPControl: React.FC<RSVPControlProps> = ({ bookKey, gridInsets }) => {
const chapters = bookData?.bookDoc?.toc || [];
const currentChapterHref = rsvpChapterHrefRef.current ?? progress?.sectionHref ?? null;
// Mirror the reader's font face/family settings on the RSVP word. The overlay
// renders in the top document where the configured (and custom) fonts are
// already mounted, so the resolved family resolves the same typeface.
const viewSettings = getViewSettings(bookKey);
const fontFamily = viewSettings ? getBaseFontFamily(viewSettings) : undefined;
// Use portal to render overlay at body level to avoid stacking context issues
const portalContainer = typeof document !== 'undefined' ? document.body : null;
@@ -549,6 +556,7 @@ const RSVPControl: React.FC<RSVPControlProps> = ({ bookKey, gridInsets }) => {
controller={controllerRef.current}
chapters={chapters}
currentChapterHref={currentChapterHref}
fontFamily={fontFamily}
onClose={handleClose}
onChapterSelect={handleChapterSelect}
onRequestNextPage={handleRequestNextPage}
@@ -78,6 +78,12 @@ interface RSVPOverlayProps {
controller: RSVPController;
chapters: TOCItem[];
currentChapterHref: string | null;
/**
* Resolved CSS font-family for the displayed word, mirroring the reader's
* font face/family settings. When undefined, the word keeps the monospace
* fallback. See getBaseFontFamily in utils/style.
*/
fontFamily?: string;
onClose: () => void;
onChapterSelect: (href: string) => void;
onRequestNextPage: () => void;
@@ -88,6 +94,7 @@ const RSVPOverlay: React.FC<RSVPOverlayProps> = ({
controller,
chapters,
currentChapterHref,
fontFamily,
onClose,
onChapterSelect,
onRequestNextPage,
@@ -710,8 +717,17 @@ const RSVPOverlay: React.FC<RSVPOverlayProps> = ({
{/* Word display */}
<div
className='rsvp-word relative flex min-h-16 w-full items-center justify-center whitespace-nowrap px-2 py-2 font-mono font-medium leading-none tracking-wide sm:min-h-20 sm:px-4 sm:py-4'
style={{ fontSize: `${currentFontSize}rem`, letterSpacing: wordLetterSpacing }}
className={clsx(
'rsvp-word relative flex min-h-16 w-full items-center justify-center whitespace-nowrap px-2 py-2 font-medium leading-none tracking-wide sm:min-h-20 sm:px-4 sm:py-4',
// Fall back to a fixed-width font only when the reader has no
// configured font face/family to apply.
!fontFamily && 'font-mono',
)}
style={{
fontSize: `${currentFontSize}rem`,
letterSpacing: wordLetterSpacing,
fontFamily,
}}
>
{currentWord ? (
isCJKWord && highlightWholeWord ? (