From 6fbe3b0794018592c3974b787963e9fb28b46453 Mon Sep 17 00:00:00 2001 From: Fendy Date: Tue, 8 Sep 2026 20:37:20 +0800 Subject: [PATCH] fix(web): active lib highlight, rail a11y names, card focus ring, AA accent + destructive tokens, touch-reachable menu, brand wordmark, chrome unify --- docs/CHANGELOG_web.md | 4 +- frontend/src/components/app-shell.tsx | 59 ++++++++++++++------------- frontend/src/components/ui.ts | 2 - frontend/src/index.css | 6 +++ frontend/src/pages/Shelf.tsx | 22 +++++----- frontend/src/pages/admin/Users.tsx | 2 +- frontend/test/ui.test.ts | 12 ------ 7 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 frontend/test/ui.test.ts diff --git a/docs/CHANGELOG_web.md b/docs/CHANGELOG_web.md index c132378..7d21458 100644 --- a/docs/CHANGELOG_web.md +++ b/docs/CHANGELOG_web.md @@ -34,8 +34,8 @@ The format loosely follows Keep a Changelog and can be adapted to the team's hab - The web UI was rebuilt on a shadcn/Radix design system: semantic light/dark theme tokens with a system/light/dark switcher (remembered), a persistent app shell (desktop sidebar, tablet icon rail, mobile bottom tabs), a shelf driven by URL-state search/sort/group controls with a flat-first grid and menu-based card actions (confirm dialogs replace native popups), and rebuilt login/admin pages. Readers keep their current chrome and every reading interaction this round; their restyle lands next. - 网页 UI 以 shadcn/Radix 设计系统重做:语义化明暗主题 token + 系统/浅色/深色切换(记忆);常驻应用壳(桌面侧栏、平板图标栏、手机底部标签);书架改为 URL 状态驱动的搜索/排序/分组工具条,默认平铺网格,卡片操作收进菜单(删除改为对话框确认);登录页与 admin 两页同步重做。阅读器保持现状,改版随下一期到达。 -- Shelf rebuilt into a content-first library UI: responsive cover grid (auto-fill 3:4 posters) with a pages/size caption, color-coded format badges, in-cover progress bars, staggered entrance animations, skeleton loading, and dedicated empty/error/searching states; 继续阅读 is now a horizontal resume strip (cover + progress + library); login got a brand card with icon-adorned inputs; toasts animate with icons; missing covers render a serif-initial placeholder instead of an endless pulse. -- 书架重构为「内容优先」的现代书库界面:响应式封面网格(自适应列宽、3:4 海报),封面下方显示页数/大小、格式彩色徽章、封面内嵌进度条、卡片错峰入场动画、骨架屏加载与空/错/搜索无结果专属状态;「继续阅读」改为横向续读卡片条(封面+进度+所属库);登录页升级为主视觉卡片与带图标输入框;Toast 带动画与图标;无封面书籍以衬线首字占位,不再无限脉冲。 +- Shelf rebuilt into a content-first library UI: responsive cover grid (auto-fill 3:4 posters) with a pages/size caption, color-coded format badges, in-cover progress bars, staggered entrance animations, skeleton loading, and dedicated empty/error/searching states; 继续阅读 is now a horizontal resume strip (cover + progress + library); login got a clean brand card; toasts animate with icons; missing covers render a serif-initial placeholder instead of an endless pulse. +- 书架重构为「内容优先」的现代书库界面:响应式封面网格(自适应列宽、3:4 海报),封面下方显示页数/大小、格式彩色徽章、封面内嵌进度条、卡片错峰入场动画、骨架屏加载与空/错/搜索无结果专属状态;「继续阅读」改为横向续读卡片条(封面+进度+所属库);登录页升级为主视觉卡片;Toast 带动画与图标;无封面书籍以衬线首字占位,不再无限脉冲。 - .txt chapter splitting no longer recognizes volumes: 「第X卷/部」 lines stay plain body text and volume-only books fall back to pseudo-sections as before; chapter switching moves from the dropdown to a 目录 sidebar overlay that highlights the current chapter, scrolls it into view, and closes on click-outside or after picking a chapter. - .txt 章节切分不再感知卷:「第X卷/部」行只作为正文文本,只有卷的书和以前一样退回伪章节切分;换章从下拉框改为「目录」侧边栏浮层——当前章高亮并自动居中可见,点击空白处或选定章节即收起。 diff --git a/frontend/src/components/app-shell.tsx b/frontend/src/components/app-shell.tsx index fb683a8..9352366 100644 --- a/frontend/src/components/app-shell.tsx +++ b/frontend/src/components/app-shell.tsx @@ -1,6 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { BookMarked, BookOpen, FolderCog, LogOut, Users } from "lucide-react"; -import { NavLink, Outlet, useLocation } from "react-router-dom"; +import { Link, NavLink, Outlet, useLocation } from "react-router-dom"; import { api } from "@/api/client"; import { useAuth } from "@/auth/AuthContext"; import { Button } from "@/components/ui/button"; @@ -10,31 +10,32 @@ import { ThemeToggle } from "./theme-toggle"; import { cn } from "@/lib/utils"; import type { ReactNode } from "react"; -function Brand({ compact }: { compact?: boolean }) { +function Brand() { return ( - + - {!compact && BookLib} - + BookLib + ); } -function NavItem({ to, icon, label, search }: { to: string; icon: ReactNode; label: string; search?: string }) { +/* NavLink 不可用:其 isActive/aria-current 前缀匹配 "/" 会无视 search,导致 ?lib=N 全部同时高亮 */ +function NavItem({ to, icon, label, active }: { to: string; icon: ReactNode; label: string; active: boolean }) { return ( - - cn( - "flex items-center gap-3 rounded-md px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground", - isActive && "bg-secondary font-medium text-foreground", - ) - } + {icon} {label} - + ); } @@ -43,12 +44,13 @@ export function AppShell() { const loc = useLocation(); const libsQ = useQuery({ queryKey: ["libraries"], queryFn: api.listLibraries }); const libs = libsQ.data ?? []; + const libParam = new URLSearchParams(loc.search).get("lib"); const account = ( - -
+
- 打开 + 打开 {isAdmin && ( <> - + 删除 @@ -74,7 +74,7 @@ function BookCard({ book, index, onOpen, onDelete }: { book: Book; index: number function SectionHead({ label, count }: { label: string; count: number }) { if (!label) return null; return ( -

+

{label} {count} @@ -148,7 +148,7 @@ export default function Shelf() { return (
-
+
setQRaw(e.target.value)} /> @@ -163,7 +163,7 @@ export default function Shelf() { 排序 {SORTS.map((k) => ( - setParam("sort", k === "added" ? "" : k)} className={k === sort ? "font-medium text-accent" : ""}> + setParam("sort", k === "added" ? "" : k)} className={k === sort ? "font-medium text-accent-strong" : ""}> {SORT_LABEL[k]} ))} @@ -190,7 +190,7 @@ export default function Shelf() { {lib === LIB0 &&

{`当前筛选:${libName}`}

} {(libsQ.data ?? []).length === 0 && !libsQ.isPending && !searching && ( -

+

还没有书库。admin 请到「库管理」建库并扫描。

@@ -215,7 +215,7 @@ export default function Shelf() {