154 Commits
Author SHA1 Message Date
hectorqinandClaude 1aff7dfb8f feat(web): remember list scroll on shelf and library, one arrow for selects
- shelf and library restore the scroll position when coming back from a
  book; the scroll-memory hook moves to shared/ and takes the container
- every native select draws the same currentColor arrow instead of none
  or the platform's own
- service configuration: an explained empty state instead of an empty
  select when the server returns no groups
- home page test waits for the series title instead of a fixed 20ms

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-10 04:32:24 +00:00
hectorqinandClaude d93a62f735 feat(web): live global search, compact phone shelf header, one back button
- search page: searches as you type (debounced, replace history), keeps
  recent searches per account on the device, "/" focuses search, the
  sidebar field steps aside on the search page, and phones get an entry
  from the home header (there was none)
- shelf on phones: 书库/书源 move into the title row so the first cover
  starts ~50px higher
- back button of 书库, 文件管理 and 书源 uses the media page frame's geometry
- reader settings: 点击区域 and 电纸书预设 fold away below the rows that
  change between books
- ui:review now covers login, home, search and a dark shelf, and asserts
  the back button geometry and the phone shelf's first cover position

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-10 04:00:20 +00:00
hectorqinandClaude 99361a46dd feat(web): readable errors, sync status page, folder picker for moves
- describeError: show the server's own Chinese wording, replace transport
  and proxy diagnostics with what kind of failure it was; used by search,
  shelf, library, sources and the media page frame
- account page gains a sync panel (state, reason, last success, retry);
  the sidebar and 我的 link to it when sync is offline or failing
- per-route document titles; the reader uses the book title
- reader says when the restored position came from another device
- file manager: pick a move destination by walking folders instead of
  typing a library-relative path

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-10 03:46:34 +00:00
hectorqin dca46d909e perf: harden sync and authentication and defer route loading 2026-11-08 05:17:42 +00:00
hectorqin eeb508f880 feat(media): add global and per-user controls with reading-only mode 2026-11-07 12:09:26 +00:00
hectorqin 7e66582f84 perf(pdf): render with PDF.js and authenticated range loading 2026-11-07 11:21:27 +00:00
hectorqinandClaude Opus 5.5 4ea2ea6096 fix(web): media browsing, lists, player queue and query cache regressions
- 影音浏览 preferences finally take effect: cover density (phone 3/2 columns),
  default catalog sort, the continue card (channel and 首页) and the 来源库
  selector, read live from the per-device preferences.
- A catalog or favorites page past the end clamps to the last page instead of
  showing nothing / claiming there are no favorites.
- 演播者 shows the 有声书 category tabs (shared CatalogTabs).
- The channel ⋯ menu offers 媒体库管理 only to administrators.
- History and search restore their scroll on back (also back after forward);
  a detail opened from search returns to the search; browser Back between two
  searches shows the earlier search again.
- The player's queue re-renders after 上移 / 移除.
- Media writes invalidate the cached queries they change (editions, metadata,
  hierarchy, libraries, recognition…); item details are always rechecked, so
  刷新资源状态 sees a rescan.
- Aborts surface as ApiError('aborted') everywhere (including body reads), so a
  shared request aborted by one caller is retried for a live one and never shows
  "signal is aborted without reason".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 23:51:33 +00:00
hectorqinandClaude Opus 5.5 a0a0fb5f2f feat(web): 偏好设置 shows its four kinds as tabs, one at a time
外观与主题, 书架, 影音浏览 and 播放 were four stacked sections on one long page.
They are now tabs (role=tablist, arrow keys, one tab stop): only the selected
panel renders, ?section= picks it (外观与主题 when absent) and switching replaces
the history entry, so back still leaves for 我的.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 23:51:33 +00:00
hectorqinandClaude Opus 5.5 b8e5102669 feat(web): 我的 opens every row as a page; preferences on one page
我的 mixed sheets (书架显示, 系统设置, OPDS nested in the shelf sheet) with pages
(外观与主题, 影音浏览, 播放), and the personal preferences were four separate rows.

- 偏好设置 (#/me/settings) holds 外观与主题, 书架, 影音浏览 and 播放 as sections of
  one page, with a jump bar and ?section=; every control applies at once (影音浏览
  loses its save button and unsaved-changes prompt). The shelf's gear still opens
  the same fields (ShelfSettingsFields) as a sheet shortcut.
- 系统设置 becomes three admin pages of 我的 › 管理: 用户管理, 注册与邀请, 服务配置
  (#/me/admin/*, lazily loaded).
- 连接外部阅读器 (OPDS) is a page (#/me/opds) under 账号, also reached from the
  书架 section and the shelf sheet; back returns to whoever opened it.
- Old /me/theme|browse|playback and /media/:channel/settings[/:panel] redirect to
  the matching section; /me/account keeps its page.
- Drops MediaSettingsPage and the CSS only it used; docs follow the new paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 15:12:40 +00:00
hectorqinandClaude Opus 5.5 df93900236 feat: 首页 continue cards share one shape; recently added / random media rows
- Books, video and audiobooks in 「继续」 are the same card (cover, work title,
  episode/chapter, progress), most recent first, up to two books. An episode
  resolves to its series for title and poster instead of reading "第 1 集".
- New rows 看点什么 / 听本书 / 听张专辑, each switching between 最近添加 and
  随便看看 (with 换一批); hidden when the channel is empty or unavailable.
- Server: browse accepts sort=random and sort=added (newest available file
  mtime across the work and its episodes/tracks; no schema change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:24:43 +00:00
hectorqinandClaude Opus 5.5 3b1f8b46dc feat: 时长统计 — reading, watching and listening time by day, month, year and work
Reading time is counted by the reader: only while the page is visible and the
reader has turned, scrolled or tapped within three minutes, or while 朗读 plays.
It is kept per book per 15-minute UTC slot and per device install, saved
locally until sent, and stored as MAX(stored, reported) in the new
reading_time table, so a replayed report never counts twice.

Media time is derived on the server from the progress saves the web player and
Android Media3 already send: the smaller of the wall-clock gap and the position
advance between two saves of one session, gaps over 30s ignored. It is stored
in media.db (media_watch_time), so neither client changes.

我的 → 时长统计 shows a stacked bar per day (one month), month (one year) or
year, with a legend that filters by kind, a table view and the works ranked for
the range or the bar picked. Administrators also get every account's totals.
Chart colours are new mode-level tokens (--color-chart-1..4), validated for
colour-vision separation on both surfaces. The React UI review now covers the
page and names the overflowing elements when a screen overflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:55:52 +00:00
hectorqinandClaude Opus 5.5 a904bb5a83 feat(web): 点击区域设置 — a 3×3 tap grid with a per-cell action
The two-way 左退右进 / 左进右退 switch becomes nine cells, each 上一页, 下一页,
菜单, 上一章, 下一章 or 无操作, edited as a small page in 阅读设置 → 翻页 with
左右互换 and 恢复默认. The default grid is the old standard layout and a stored
`tapZone: 'reversed'` is folded into its grid. At least one 菜单 cell is kept,
right-to-left books mirror the grid, and the gesture layer now reports the row
as well as the column, from layout geometry like before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:27:51 +00:00
hectorqinandClaude Opus 5.5 21e2509c86 feat(web): 阅读记录 in 书籍信息 brings back an overwritten reading position
Tapping the title opens 书籍信息, which now ends with 阅读记录: the places this
account has been in the book on any device, newest first, each with its
chapter, percentage, device and time. The position on screen is pushed
before the list is read, so its entry is marked 当前; pressing another entry
goes back there and saves it as the position. Offline the section says the
history cannot be read; a failed read offers 重试.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:51:42 +00:00
hectorqinandClaude Opus 5.5 842331684a feat(server): keep a per-book reading-progress history
reading_progress keeps one row per book and last-writer-wins, which answers
"where am I" and loses "where was I": a stale device, a mistapped 目录 entry
or a clock running ahead overwrote the place for good (P0: 进度历史).

Every valid progress write now also lands in reading_progress_history,
including a write that lost the merge. A device reading on through one
chapter updates its latest entry (same chapter, within 5% and 30 minutes)
instead of adding rows; another chapter, a jump, another device or an
out-of-order write is a new entry. The newest 50 per book are kept.
GET /api/v1/sync/progress/:bookId/history lists them, newest first, for the
signed-in account only.

The table is new (CREATE TABLE IF NOT EXISTS), so the schema change is
additive and older builds sharing the database ignore it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:51:42 +00:00
hectorqinandClaude Opus 5.5 0115dbd81a feat(web): in scroll mode, pulling past a chapter's end opens the next one
Taps and keys already crossed chapters, but a reader scrolling by hand reached
the last line and had to find 下一章 (#702, #698). A touch pull of more than
72px that starts at the end of a chapter, or wheel scrolling past it, now
opens the next chapter; the same at the top goes back. Only a gesture that
begins at the edge counts, so arriving at the end does not turn by itself,
and a cooldown keeps trackpad momentum from turning a second chapter. Touch
events are used because the browser cancels pointers once it scrolls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 02:56:36 +00:00
hectorqinandClaude Opus 5.5 d8b3491e95 chore(web): remove the reading engine and helpers nothing runs
An import graph from main.tsx shows a previous reader still in the tree:
render/{reader,paged,pdf,text,container,theme}.ts with lib/{events,locator}.ts,
ui/settings.ts and bridge.ts — superseded by ReaderScreen/ReaderView and
never imported. Also unreachable: render/paginator.ts, render/window.ts and
ui/shelf-order.ts (only their own tests used them; the shelf sorts on the
server), formats/remote-zip.ts (re-exported, never called; chapters are
fetched per section), ui/source-capabilities.tsx and the sources pages barrel.

Inside live modules: PrefetchCache (only shouldPrefetch is used),
textToParagraphs / paragraphElement / endsAtParagraphBreak (the streaming
TXT reader's carry), txt.ts's textToHtml alias, and ChunkCursor. Their tests
go with them; the node:test list, the Vitest excludes and the docs that
named these files are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 09:49:37 +00:00
hectorqinandClaude Opus 5.5 12fbe0cbcd chore(web): remove the icon font toolchain and unused icon names
The UI draws Lucide SVGs through ui/icon.tsx and has not loaded the icon
font for a while; the font, its code-point table and the generator only fed
their own tests. Removed: tools/icons (generator, glyph paths, stroker),
src/styles/reader-icons.ttf, src/ui/icon-names.ts, the `icons` npm script and
the font/metrics tests. icons.test.ts keeps the contracts that still apply:
no borrowed names (now checked against ICONS), only icon.tsx imports
lucide-react, the .icon rule and icon buttons. The .icon rule drops its
font-era declarations.

ICONS loses the names nothing draws (the Font Awesome spellings kept as
aliases, add-circle, film, line-height, indent, sign-out); the reader's 朗读
button is `read-aloud` rather than `volume`. Docs and CLAUDE.md describe
adding an icon in icon.tsx.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 09:48:04 +00:00
hectorqinandClaude Opus 5.5 97429ddeed refactor(web): ui/icon.tsx owns the icon set — names, sizes and stroke
IconName came from the legacy font's code-point table, so a new icon meant a
new font glyph, while 59 files imported lucide-react directly with their own
glyphs, 17 sizes and strokes from 1.4 to Lucide's default 2. icon.tsx now
defines every name (in the product's words: video, audiobook, lock,
folder-browse, …), each drawn by one Lucide glyph; IconName is its keys.
Icon gains a six-step size scale (14–32), `filled`, and `strong` for the
current navigation item; the stroke is 1.8 everywhere, 1.4 at the 32px
empty-state size. ui.md §1 describes the rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 07:26:51 +00:00
hectorqinandClaude Opus 5.5 4a6b7a3858 feat(web): 书库 can be browsed into its subfolders
The browsing page asked /library/browse for the folder's entries but drew only
its crumbs, and the crumbs appear only once you are already in a subfolder:
a shareable #/library?path=科幻 scope existed, but nothing on screen led to it.
Subfolders are now a row of chips under the search field; one press narrows
the scope a level and the crumbs widen it again. Folders excluded by a scan
rule are not shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 05:04:02 +00:00
hectorqinandClaude Opus 5.5 c1056743ad feat: the reader shows AI 总结 only when the server can answer it
The top bar always drew AI 总结, although AI is off by default, so most
readers pressed it only to be told AI_DISABLED (ui.md §4/§5: a control the
reader cannot use is not drawn). GET /api/v1/ai/capabilities tells any signed-in
account whether chapter summaries are enabled and configured, without exposing
the configuration; the reader asks once per book and leaves the button out
when the answer is no. Against an older server (404) the button stays.

The busy line while a summary is generated is a global notice like every other
one, instead of a hand-drawn .floating-notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:36:01 +00:00
hectorqinandClaude Opus 5.5 923adcc006 docs(ui): describe the reader's footer, slider, failure state and keys as built
§3.10 gains the 工具 button and the 朗读中 state, the comic exception to the
page slider, the open-failure state with 重试/返回, and the keyboard map.
§3.9 no longer mentions a 快捷 row the settings panel does not have; the TXT
rows live under 更多排版.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:19:00 +00:00
hectorqinandClaude Opus 5.5 120827bad6 docs: bring every document in line with the code
Audit of README, docs/, android/README.md, android/contract.md and CLAUDE.md
against the current server, web and Android code: V3 navigation and settings
locations, Docker Hub release images, GitHub clone URL and CI, missing API
endpoints and error codes, architecture of lazy chunks/themes/sources, media
pages and routes, the Android bridge contract, and the new file-management and
member-upload permissions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:03:45 +00:00
hectorqinandClaude Opus 5.5 f50ce1b765 fix(web): rework the media library manager and scan & scrape pages
媒体库管理: 新建媒体库 and 扫描与刮削 are visible above the list, an empty
state offers 新建媒体库, and cards get a channel-coloured kind icon, a
指定用户 tag, a one-line directory, missing files in the warning colour,
开始扫描 for never-scanned libraries, and 扫描记录 in the ⋯ menu. Cards
take the full width when the server has no 扫描与元数据 switches.

扫描与刮削: scan and AI scan share one toolbar (library in ?library=,
refresh, a single primary action) instead of duplicated buttons; scan
records show their time, keep title/state/action on one line, put the
failure in a warning box followed by 重试, and have an empty state.
刮削 preselects the page's library, lays the form out in two columns
when wide, and keeps 已选 N and 开始批量匹配 pinned under the item list.

Styles live in features/media/styles/manager.css, loaded after v2.css.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 08:10:45 +00:00
hectorqinandClaude Opus 5.5 ff8d289185 docs(web): source chapter cleaning is decided by the plugin's source type
The Legado plugin now cleans by bookSourceType (text paragraphed, image
sources untouched); the reader only re-typesets text-only chapters, which
never includes a comic chapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 06:34:11 +00:00
hectorqinandClaude Opus 5.5 60d4b41bf1 fix(web): typeset book-source HTML chapters like TXT
Source chapters are scraped fragments (`<div>  text<br><br><br><br>`) served
as text/html, so the TXT cleanup never ran: every paragraph was followed by
three empty lines and the indent setting did nothing. Like legado-E, a
text-only chapter of a `chapters` book is read back as characters when it
is loaded (htmlToText) and typeset by the TXT path. Done at read time, so
chapters the server cached earlier are cleaned too; chapters with images
or tables keep their markup and EPUBs never take this path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 06:20:27 +00:00
hectorqinandClaude Opus 5.5 52b757bbd3 fix(web): clean web-page markup out of TXT chapters before typesetting
Novels saved from a site keep the page's <div>, <br/> and &nbsp; in the
TXT. Escaped, the tags were drawn as text and the chapter ran together
as one paragraph. cleanTextMarkup turns block tags into line breaks,
drops inline tags, decodes common entities and treats whitespace-only
lines as blank, before paragraphs and the chapter heading are inferred.
Only real tag names match; other markup is still escaped as text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:51:18 +00:00
hectorqinandClaude Opus 5.5 6514387f9e fix(web): PWA orientation, media swipes and follow-finger page turns
- Manifest: drop orientation "any" (Chrome maps it to the full sensor, so the
  installed app ignored the phone's rotation lock); the service worker serves
  the manifest network-first so installed apps can pick up manifest changes.
- Media: derive the channel from the path for static routes (演播者 showed 影视);
  keep the tab bar on audiobook/narrators; a channel with no library swipes a
  whole channel at a time.
- Reader (paged mode): the column strip follows the finger; release turns on a
  flick or past 30% of the width, otherwise springs back; chapter edges fall
  through to the normal turn.
- PWA smoke test stubs the current window.__readerFlush hook.
- Docs: ui.md §3.8 drag-follow and media swipe rules; pwa.zh-CN.md orientation
  and manifest caching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:05:18 +00:00
hectorqinandClaude Opus 5.5 2f784d1d4e fix(web): mobile PWA style review fixes
- Reader (scroll paging): mask the line the page's bottom edge cuts through
  (clip-path, --reader-page-mask) so no page shows a half-sliced last line;
  re-measured after page-turn animations.
- Landscape sidebar layout: the sidebar owns the left safe-area inset, the page
  column the right one; fix .shelf-head's swapped left/right padding; reader
  top/bottom bars respect left/right insets.
- Guard all :hover rules with (hover: hover) and (pointer: fine), enforced by
  test/design-tokens.test.ts.
- PWA update banner sits above the phone tab bar and reader toolbar.
- Top safe-area on sources header and phone media pages; 16px sidebar search
  on touch (iOS focus zoom); larger hit areas for chips and cover controls.
- Manifest: unified launch colour #FDF9F6, maskable icon, drop
  window-controls-overlay; regenerate branding assets.
- Docs: ui.md safe-area/touch/hover/reader rules, pwa.zh-CN.md icons and
  window notes, CLAUDE.md stale bundle notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 07:35:51 +00:00
hectorqinandClaude Opus 5.5 a634e9e955 fix(web): every search field says its scope
The side-nav global search and the shelf's own field sat side by side on
desktop and both read "搜索书名、…"; the shelf one was even labelled
搜索书库 while it only filters the shelf.

- Global (side nav, /search): 搜索书籍和影音 — also no longer truncated in
  the 216px rail.
- Shelf: 在书架中筛选书名、作者、系列, labelled 筛选书架; results are 筛选结果.
- Library: 在书库中搜索书名、作者.
- ui.md §4 records the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:15:20 +08:00
hectorqinandClaude Opus 5.5 81ec4ba6bc refactor(web): seven role-named type sizes replace ~50 literal font sizes
ui.md's four type roles only governed the reading side; the media sheets
used every integer from 8 to 24px plus dozens of rem values. Every font size
up to 24px now reads one of --text-display/title/body/ui/label/meta/micro.

- --text-ui (14px) is the media list/settings body; --text-display (22px) the
  page hero; --text-micro (10px) only for cover/icon badges.
- --text-meta moves 0.72rem -> 0.75rem, so the 200+ media 12px lines land on
  it unchanged and the reading side's meta grows by half a pixel.
- Inputs that were 16px land on --text-title (16.8px), keeping the iOS
  focus-zoom floor; ui.md says so explicitly.
- Sizes above 24px (player initials, hero numerals) and the decorative serif
  shorthands stay literal; the token test rejects literals <= 24px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:10:31 +08:00
hectorqinandClaude Opus 5.5 7e8b0ea278 feat(web): placeholder covers take a stable per-title tint
A shelf of TXT books (or a media library before scraping) was a wall of
identical panels, so finding one meant reading every placeholder. Titles now
hash to one of twelve hues 30° apart (ui/cover-tint.ts), mixed 10-20% into
the theme surface, so the tint follows light/dark and the text on it keeps
its contrast. Shelf, library and media covers share it. ui.md §2.7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:10:46 +08:00
hectorqinandClaude Opus 5.5 c5125792d7 refactor(web): six-step radius tokens replace ~40 literal corner sizes
The stylesheets used 2/3/4/5/6/7/8/9/10/11/12/13/14/15/18/19/20/22/24px plus
a dozen rem values for corners, so the same kind of control differed by a
pixel or two between neighbouring screens. --radius-xs/sm/md/lg/xl/pill
(4/8/10/14/20/999px) now carry every corner; --reader-radius is --radius-md.
Controls whose radius was half their height (switches, the role tag, the
home hero action, the nav icon pill) use --radius-pill so they stay capsules
if their height changes. ui.md §2.6 documents the scale; a test fails on any
new literal border-radius.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:06:19 +08:00
hectorqinandClaude Opus 5.5 92cfabe740 feat(web): primary buttons fill with brand-text so they outweigh secondaries
The light themes' brand fill (#F2B79F etc.) is softer than the white
secondary button beside it, so 保存/播放 read as the quieter choice. Primary
buttons now use brand-text with bg on it, the same recipe as danger, and a
pair the token spec already holds to 4.5:1 in every theme. brand stays for
state fills (selected, progress, switches). Spec updated (rule 6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:54:48 +08:00
hectorqin 92f26ba122 feat(web): ⋯ menus drop down; drop 影音设置; rebuild 插件管理
- ActionMenu is an anchored dropdown on every width (popover, no title bar or backdrop; closes on pick, Escape, Tab or a press outside; arrow keys). ActionSheet stays for question-style picks. Styles in styles/action-menu.css.
- Remove the media settings home and its ⋯ entry: panels live only at /me/:panel; /media/:channel/settings[/:panel] redirects to 我的. Library and task pages return to 我的 or the channel.
- 插件管理: one list with status tags and a per-row ⋯ menu (启用/停用, 卸载 with confirmation); install moves into a Sheet (npm / local file, trust switch). Old plugin styles removed from reader.css.
- 首页: drop the redundant 继续阅读 pill on the reading card.
- Docs, unit tests and ui:review updated (stub admin settings in the media review).
2026-10-21 15:26:49 +08:00
hectorqin 2d7e858ecc refactor(web): drop Mantine; one ActionMenu for every ⋯ menu
- Replace every Mantine component with the toolkit (Button, Callout, IconButton) or plain markup; remove MantineProvider, its stylesheet and the --mantine variable bridge; keep its form-font reset in reader.css. Uninstall @mantine/core and @mantine/hooks.
- ActionMenu/ActionSheet (ui/action-menu.tsx): the file manager's Mantine Menu, the shelf pick dialog and the media <details> menus (catalog, narrators, detail, libraries, library rows, folders, chapters, queue, resource panel, favorites filter) are one compact Sheet menu.
- Toolkit Callout for page-level messages; operation failures go to FloatingNotice; .page-state for whole-page loading/error.
- Shared SourcesHeader and SourceBookDetailSheet; source editor and credentials pages rebuilt without Mantine, deleting a source now asks first.
- Remove dead code: SettingsPage (unrouted), MiniPlayer, ui/dom.ts (formatters moved to lib/format.ts), the unused metadata form dialog, useAppThemeDark.
- ui:review react-app passes again: fixed stale steps (/settings, mini player, login race, missing-edition detail).
2026-10-21 11:38:12 +08:00
hectorqin fbb6dc65cf feat(ui): one Sheet for every overlay; drop 来源与刮削
- ui/sheet.tsx Sheet/ConfirmSheet replace the centred Modal, SweetAlert confirms, DialogView overlays, the reader Panel, the shelf settings floating card, the two media dialogs and Mantine Modal.
- Phones: bottom sheet. Wide windows: right-edge drawer; compact sheets (confirm, prompt, pick) open next to the tap or focused control.
- One set of exits (close, Escape, backdrop), 关闭 label, focus starts on 取消, danger confirms.
- Remove sweetalert2 and the unused @mantine/notifications.
- Remove the 来源与刮削 media settings panel and its entries in 我的 and 影音设置; TMDB and MusicBrainz stay configurable in 系统设置 > 服务配置.
- docs/ui.md 3.7 describes the rule; tests and review scripts follow the new markup.
2026-10-21 07:30:27 +08:00
hectorqin 05762208d9 docs(prototype): 我的 as the settings hub and one page column
- 8 我的(手机): 内容 / 偏好 / 管理 / 账号 as in the app; the list scrolls above the tab bar.
- New 20b 我的(桌面 · 设置中心): groups two to a row in the page column.
- Desktop 首页 / 书架 / 媒体库管理 use the shared 32px gutter (was 40px).
- 设计系统 notes the shared page column and that settings live in 我的.
2026-10-20 18:27:11 +08:00
hectorqin 1f534ee4c3 feat(shell): one page column for the four tabs, 我的 as the settings hub
- 首页/书架/影音/我的 share --page-measure and --page-gutter, so content edges stay put across tabs at every width; 我的 lays its groups two per row on wide screens.
- 我的 gathers every setting (偏好 / 管理 / 账号); its panels live at /me/:panel and return to 我的.
- 影音设置 keeps only media settings and links to 我的; 来源与刮削 is now reachable.
- 系统设置 moves from the shelf header to 我的 → 管理.
2026-10-20 14:58:00 +08:00
Kiro Agent f15365d03c feat(shelf): 在读 / 想读 / 已读 and 分组 (prototypes 2 and 19)
Server: user_books gains reading_status. The shelf list computes each
book's readingState — an explicit 在读/已读 mark wins, otherwise progress
decides (>=99% 已读, started 在读), and 想读 gives way once reading starts —
filters by ?status=, and PUT /api/v1/books/:id/reading-status sets or
clears the mark.

Client: chips 全部/在读/想读/已读 and 分组 (a series or a tag from the
shelf's facets) above the toolbar, all kept in the URL; a state label on
each cover; the ⋯ menu marks 想读/在读/已读, clears the mark, or takes the
book off the shelf.

Docs: API, UI and media user guide for this and the other V3.1 gaps.
2026-10-20 04:47:06 +00:00
Kiro Agent c30e07c270 feat(reader): reading papers as a reader setting
白纸 / 暖黄 / 浅绿 / 灰调 / 夜间 (prototypes 4 and 23) tint only the reading
page — paper, ink, readouts — while the chrome keeps the reading theme.
Stored per device in AppSettings (paper, paperFollowsDark), set from the
reader's 外观 sheet or 外观与主题. 夜间 makes the reader dark; a light paper
gives way to the theme's dark values while the app is dark unless
跟随系统深色模式 is off. Old sepia/green/dark page themes migrate to the
matching paper. Tokens are mirrored in TS and CSS and held to contrast by
theme-tokens.test.ts.
2026-10-20 04:26:51 +00:00
Kiro Agent 858a7479bf feat(theme): V3.1 semantic tokens, four themes and a reading theme
Implements docs/prototypes/Reader V3 主题语义令牌.md (v3.1):

- 暖陶 (default), 雾蓝, 青绿 app themes plus the reading-only 黑白灰, each
  light and dark. Components read --color-* tokens from
  styles/theme-tokens.css, selected by data-theme + data-mode; the
  TypeScript mirror lives in shared/theme/theme.ts.
- Brand is split per the spec: brand/ch-* only fill (with on-brand/on-ch on
  top), brand-text/ch-*-text for text. --reader-accent/--media-accent are now
  brand text; --reader-fill/--media-fill are brand fills. Selected segmented
  controls use brand-soft + brand-text.
- One preference per server+account (reader.theme.v2): app theme, mode
  (system/light/dark), the follow-system light/dark pair, and the reading
  theme. V3 choices migrate per spec §8 (rose falls back to 暖陶 light).
- 外观与主题 rebuilt from prototype 23: follow-system switch with the
  light/dark pair, app-theme cards with light/dark previews, and the
  reading theme (跟随应用 / 黑白灰).
- Reader: 黑白灰 is set on the reader container only; the appearance panel
  offers 跟随应用/黑白灰 and 明暗; day/night sets the mode; the e-ink preset
  uses 黑白灰 light.
- Video and music players wear the current theme's dark values (§4.7).
- Removed the legacy light/sepia/green/dark palettes and the
  <html data-theme> writes from reader settings, which would now collide
  with data-theme="green".
- Tests: theme-tokens.test.ts keeps CSS and TS values identical and checks
  the §5 contrast pairs for all 8 palettes; theme.test.tsx covers migration,
  resolution, the controller and the settings page.
2026-10-20 04:05:35 +00:00
hectorqin be0f20064f 新增V3原型和主题语义令牌 2026-10-20 10:20:32 +08:00
hectorqin 5ffee3db51 feat(media): add play modes and customizable system themes 2026-10-20 10:17:18 +08:00
Claude db6ea15727 docs(ui): shell sidebar breakpoint is 701px 2026-10-19 22:33:29 +08:00
Claude c0dc54efab feat(reader): one theme for reading and media, V3 reader chrome
The book now wears the same six app themes as every other page (plus
follow-system); the separate white/sepia/green/dark reading themes are gone.
The 外观 sheet's swatches are the app themes drawn in their own colours and
the day/night rail button switches between 暖纸 and 深夜. The bottom bar
becomes a rounded sheet. 界面 is renamed 外观 in the bar, panel, review
scripts and spec.

Shelf title is 书架 in the serif face and loses the now-redundant headphones
link. docs/ui.md gains the V3 shell section.
2026-10-19 22:33:27 +08:00
Claude bf325ec072 feat(reader): reorder appearance and read-aloud sheets by use
Appearance: font size, line height, theme (drawn as swatches in each theme's
own paper and ink) and brightness now fill the first screen. Margins,
alignment, font, TXT rows and the four readouts move into closed <details>.

Read aloud: the start button is first and primary; rate gains preset steps;
pitch and volume are folded under 更多. Capability hiding is unchanged.

Spec updated in docs/ui.md; chrome tests cover order, folds and swatches.
2026-10-19 22:33:17 +08:00
hectorqin 875fec84dd fix(media): sync prototype routes and review tooling 2026-10-08 09:51:42 +08:00
hectorqin 8584d7b4e3 fix(web): align React UI with legacy reader UX
Unify media secondary page chrome, loading states, playback controls, favorites filtering, and source/library screens with the legacy reader experience.
2026-10-05 13:23:27 +08:00
hectorqin 007cc3c754 fix(web): finish media UX alignment
Align the React media detail, player, history, queue, shelf, and resource flows with the legacy UX. Refresh the migration validation record and add focused route, resource, and shelf parity coverage.
2026-10-04 11:02:23 +08:00
hectorqin c0af84a00f test(web): verify React media flows against legacy UX 2026-10-03 20:19:52 +08:00