feat(ui): warm library palette (zinc+emerald -> stone+amber) with hover/focus feedback; text reader gains paper/sepia/night themes + persisted font-size (lib/readerPrefs.ts), serif justified measure; PDF drops emoji icons for 首页/末页, CBZ pill page counter, EPUB centered spread, blur reader header with format badge, aria labels on login/search/chapter select
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const RD_THEMES = ["paper", "sepia", "night"] as const;
|
||||
export type RdTheme = (typeof RD_THEMES)[number];
|
||||
export const RD_THEME_LABEL: Record<RdTheme, string> = { paper: "纸", sepia: "米", night: "夜" };
|
||||
export const FONT_SIZES = [16, 18, 20, 22, 25, 28];
|
||||
|
||||
const KEY = "reader-prefs";
|
||||
|
||||
export interface ReaderPrefs {
|
||||
theme: RdTheme;
|
||||
sizeIdx: number;
|
||||
}
|
||||
|
||||
export function useReaderPrefs() {
|
||||
const [prefs, setPrefs] = useState<ReaderPrefs>(() => {
|
||||
try {
|
||||
const p = JSON.parse(localStorage.getItem(KEY) ?? "") as ReaderPrefs;
|
||||
if (RD_THEMES.includes(p.theme) && Number.isInteger(p.sizeIdx) && p.sizeIdx >= 0 && p.sizeIdx < FONT_SIZES.length)
|
||||
return p;
|
||||
} catch {
|
||||
/* 首次使用或存了坏值 */
|
||||
}
|
||||
return { theme: "sepia", sizeIdx: 2 };
|
||||
});
|
||||
useEffect(() => localStorage.setItem(KEY, JSON.stringify(prefs)), [prefs]);
|
||||
|
||||
return {
|
||||
prefs,
|
||||
fontSize: FONT_SIZES[prefs.sizeIdx],
|
||||
cycleTheme: () =>
|
||||
setPrefs((p) => ({ ...p, theme: RD_THEMES[(RD_THEMES.indexOf(p.theme) + 1) % RD_THEMES.length] })),
|
||||
bump: (d: number) =>
|
||||
setPrefs((p) => ({ ...p, sizeIdx: Math.min(FONT_SIZES.length - 1, Math.max(0, p.sizeIdx + d)) })),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user