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:
2026-09-07 22:37:47 +08:00
parent 2478da059b
commit 7fb01ce335
20 changed files with 281 additions and 126 deletions
+9 -6
View File
@@ -26,7 +26,7 @@ function PageImg({ url, width, onLoaded }: { url: string; width: number; onLoade
}, [url, nonce]);
if (failed)
return (
<div className="grid place-items-center gap-2 bg-zinc-900 py-8 text-sm text-zinc-500" style={{ width }}>
<div className="grid place-items-center gap-2 bg-stone-900 py-8 text-sm text-stone-500" style={{ width }}>
本页加载失败
<button className={btn} onClick={() => setNonce((n) => n + 1)}>
重试
@@ -34,7 +34,7 @@ function PageImg({ url, width, onLoaded }: { url: string; width: number; onLoade
</div>
);
if (!src)
return <div className="animate-pulse bg-zinc-800" style={{ width, height: width * EST_RATIO }} />;
return <div className="animate-pulse bg-stone-800" style={{ width, height: width * EST_RATIO }} />;
return (
<img
src={src}
@@ -102,10 +102,10 @@ export default function CbzReader({ book, initialLocator }: ReaderProps) {
// pages_url 缺失时 query 被 disabled 永久 pending → 只有真正发起了请求才显示加载态
if (countQ.isPending && !!book.pages_url)
return <div className="grid h-full place-items-center text-zinc-500">页索引加载中…</div>;
return <div className="grid h-full place-items-center text-stone-500">页索引加载中…</div>;
if (countQ.isError || !book.page_url_fmt)
return (
<div className="grid h-full place-items-center p-8 text-center text-zinc-500">
<div className="grid h-full place-items-center p-8 text-center text-stone-500">
无法解析页索引:{countQ.error instanceof Error ? countQ.error.message : "缺 page_url_fmt"}
</div>
);
@@ -138,8 +138,11 @@ export default function CbzReader({ book, initialLocator }: ReaderProps) {
})}
</div>
</div>
<div className="pointer-events-none absolute bottom-3 right-3 rounded bg-zinc-900/80 px-2 py-1 text-xs text-zinc-300">
{cur + 1}/{count}
<div className="pointer-events-none absolute bottom-3 right-3 flex items-center gap-2 rounded-full bg-stone-900/85 px-3 py-1.5 text-xs tabular-nums text-stone-200 shadow-lg ring-1 ring-stone-700/60 backdrop-blur">
<span>
{cur + 1}/{count}
</span>
<span className="opacity-60">{Math.round(((cur + 1) / count) * 100)}%</span>
</div>
</div>
);