The SwitchRow styles were scoped to `.sheet`, so on 偏好设置 › 书架 the
显示作者/显示阅读进度 switches rendered as empty transparent buttons. Unscope
them, and restore the pill over the media page's generic button rule.
主题已保存 and theme save errors were inline <p> lines; they now go through
the global notice (ui.md §3.6), one notice updated in place per panel.
ui:review captures the shelf section and measures the switches and the
floating theme notice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The real cause of the CI failure: createMemoryRouter builds a fetch Request for
every navigation, and on Node 24 (CI) undici rejects jsdom's AbortSignal
("RequestInit: Expected signal to be an instance of AbortSignal"), so no click or
Back ever navigated. Locally Node 22 accepts it. The test now mounts a plain
MemoryRouter, like every other test, and reads location / navigation type /
navigate through a probe component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI failed every test in media-list-return.test.tsx: each ran ~550-650ms, the whole
budget of settle()'s 50 x 10ms polls, because the first render is slower on the
runner than locally. settle() now polls for up to 4s (under vitest's 5s test
timeout); media-catalog-page.test.tsx had the same helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 影音浏览 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>
外观与主题, 书架, 影音浏览 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>
Every script under web/tools and web/tools/ui-review had drifted from the app
(login lands on 首页, AppNav instead of the channel menu, 我的 pages instead of
sheets, query-string paging and folders, the redesigned plugin, source and
library screens). Each is updated to the current flows; checks for features that
no longer exist are removed (the v2 settings home and media palettes, the
channel menu, the 刷新 button, debounced library search, the sheet-based 添加书源
and 书源登录, the per-script track filters, …).
media-smoke and media-review-smoke run to the end but still fail on genuine app
regressions they record (listed in their output) rather than hiding them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 偏好设置 › 打开播放器 always opened the video player; it now opens the playing
channel's player (playerPath, shared with the mini player's link).
- The player's panel and back navigation dropped item/part from the address, so a
refresh could not restore playback; the address now follows the playing part, and
a restoring link shows 正在恢复播放… instead of flashing 当前没有播放内容.
- A link that cannot be restored no longer leaves another video expanded over its
explanation; the other playback stays in the mini player.
- The first category tab of 音乐 and 有声书 was never marked current (the bare
channel path redirects to it).
- A navigation query shared by two callers handed the first caller's abort to the
second (a detail opened right after login showed "request aborted").
- The metadata and match pages' fixed header covered the sidebar at >=701px.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A book downloaded for offline reading did not open offline: nothing ever writes
books into the offline mirror, so the fallback found nothing. The reader page now
falls back to the book record the download stored in its manifest.
- 书源登录 reopened on the cached credential status after saving; saving now
refetches that source's status.
- A newly installed/upgraded/enabled/removed plugin's source types did not appear
in 添加书源 for up to five minutes; plugin changes now refetch the type list.
- The extension page did not scroll its selected tab into view on narrow screens.
- The shelf settings sheet's sticky headings stopped 14px below the header again
(the padding rule was lost when .panel-body became .sheet-body).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
我的 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>
- 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>
The page borrowed media-utility-workspace, whose layout expects the settings
screens' own .media-settings-heading, so the frame's header was left unstyled:
on desktop it sat ~100px down inside the content column instead of pinned at
the top like 影音浏览 and 播放历史. It is a content page, so it now wears
media-secondary-page as 播放历史 does.
The React UI review also stubs /api/v1/ai/capabilities, which 扫描与刮削 asks
since the AI-switch change and the media fixture does not serve.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/api/v1/ai/capabilities now also answers `scan`: 启用 AI and 启用 AI 扫描 on and
Base URL, key and model filled in. 扫描与刮削 shows the AI 扫描 tab only then; while
the answer is unknown there is no tab, a ?tab=ai link falls back to 媒体库扫描,
and a server without the endpoint keeps the tab as before. The answer is
re-asked each time the page opens, so a switch changed in 系统设置 takes effect
on the next visit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
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>
Leaving the reader left the new position to the sync debounce, and 首页's
继续阅读 and the shelf's 最近阅读 order were served from a query cache that stays
fresh for a minute, so coming back from a book showed the old chapter and
percentage (#663). Closing the reader now saves the position, pushes the
outbox, and then re-reads the home and shelf queries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a894bdd deleted .action-menu-item/-label/-icon/-check/-description and the
.sheet-list-icon/-check/-description rules as unused, but ActionMenu builds
those names as `${base}-item` (base 'action-menu' or 'sheet-list'), which the
dead-class scan did not recognise. Every ⋯ dropdown and its bottom-sheet form
lost their row layout, hover, danger colour and check mark. Both files are
restored to their state before that commit.
The class-usage test now covers every stylesheet (not only media) and
understands built names: `${expr}-suffix` with a literal prefix, and
`prefix-${expr}`. Library markup (Notyf, DPlayer) is exempt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 class names in reader.css, action-menu.css and sheet.css had no reference
in the source (the old quick-settings row, manager screen and selection bar,
catalog source heading, source capabilities and editor, action-menu and
sheet-list item parts, …); their rules, or their members of selector lists,
are removed. Classes built at runtime (indicator-*, reading-indicator-*) and
Notyf's own are kept.
The reader review no longer checks for a .library-preview-hint the browsing
page stopped drawing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LoadingPage and isApiError; the media search's back/forward restore
(restoreSearchHistory, its history tagging and saved-entry map,
clearSearchReturn, restoreSearchReturn), which nothing ever called — the
in-memory return to search still works; SeriesPlayback and its styles;
useMediaRequest, useMediaDetail, useMediaPlayback and searchQuery; and the
short route aliases at the end of MediaItemManagementPages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
All direct lucide-react imports (59 files) now render <Icon name=…>. Along
the way the set converges: settings is one gear (not Settings/Settings2),
lock one padlock (not Lock/LockKeyhole), music one note (not Music/Music2),
video one clapperboard (the library cards and 我的 used Film), clock one face;
sizes snap to the scale and the stroke is uniform; decorative icons are
aria-hidden by default (about 36 were not). Text characters used as icons
(✓ on highlight colours, + 加书签, the 目录's +/− fold) are icons too.
icons.test.ts fails if any file but ui/icon.tsx imports lucide-react.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
Eleven media rules and two in the file manager read env(safe-area-inset-*)
directly, which escapes both the Android shell (it zeroes --safe-*) and any
layer that owns an edge; they now read the variables, and design-tokens.test.ts
rejects new direct reads (the sidebar column's documented exception stays).
Media focus outlines use --focus-ring/--focus-offset like the rest of the app.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 书源与追更 tabs are a proper tablist: one tab stop, arrow/Home/End keys,
aria-controls on a labelled panel. The tab is kept in ?tab=, so a reload or
back from an editor returns to it.
- 文件管理 rows are no longer tab stops that do nothing (their check, folder
name and ⋯ are the controls); the current crumb is aria-current="location".
- 首页's 继续阅读 card is named by its visible text (title, chapter, 已读 x%),
and the sidebar account link includes the name and the connection state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The connected badge was the character 在 on a fixed green circle (read aloud
as "在"); it is now a check icon labelled 已连接服务端, while probing is
labelled 正在连接服务端. The badge and the required-field mark use the theme's
brand and danger tokens instead of #51b95b/#9aa3ae/#e66d67.
The effect that sent a fresh login to /media/video is gone: the page is
unmounted as soon as a session exists, so it never ran, and the route the
reader asked for is kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a tap may last up to 450 ms (it is decided by the finger staying put); at
260 ms a deliberate tap fell between tap and long press and did nothing.
- a long press that the system turned into a text selection no longer also
opens the toolbar over the words being selected.
- the 朗读 progress chip is announced as its progress and "从当前页重新朗读",
which is what it does, instead of "从头朗读这一章".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without an enabled source the panel repeated the tab's 搜书 as a heading, drew
a disabled 选择来源 picker and then said there was nothing to pick. It now
says there is no source yet and offers 前往书源管理; while sources are still
loading it says so instead of claiming there are none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row was an auto-fit grid of 84px columns, so a phone fitted four and 更多
wrapped onto a line of its own (a sixth button, 选集, made it worse). The
buttons now share the row in equal columns however many there are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- scan and AI-scan job lists refresh every 5 s only while a job is queued or
running, every 30 s otherwise; the AI list no longer adds its own 2.5 s
timer on top of the query's.
- AI batch details that fail to load say so instead of showing an empty list;
expand buttons carry aria-expanded.
- 刮削 errors show the message, not "Error: …".
- 按作品信息自动匹配 sits beside 搜索候选 instead of inside a 自动匹配 disclosure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Adding a bookmark took four steps (toolbar → 工具 → 笔记 → 加书签) and nothing
on the page said whether it already had one. The top bar now has a bookmark
toggle: pressed and filled when the page on screen carries a bookmark (the
same exact-position test 阅读工具 uses), one press adds or removes it, and the
change syncs like any other note.
The bookmark glyph joins the icon set. tools/icons/build.mjs now emits the
alias table from paths.mjs (ALIASES) instead of dropping it, so running
`npm run icons` reproduces icon-names.ts. IconButton takes `pressed` for
toggles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comic's chapters are its pages, so "this chapter's pages" is always 1 and
the slider was permanently disabled: reaching page 150 of 200 meant scrolling
the 目录. For fixed layout with one-page chapters the slider is now the
book's page number (第 7/200 页); the label follows the thumb and the page
opens on release, so a drag decodes one image rather than every page passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slider sends one settings change per step. Each change measured the reading
position and queued its own restore, so a drag through the font-size range
re-measured a half-reflowed page many times a frame and could lose the place.
The position is now measured once before the first change of a frame and
restored once after the last; brightness, an overlay, skips both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- every transition (home, 我的, media tabs/cards, media select, action menu,
notices) is timed with --motion-duration/--motion-ease, so reduced motion
reaches them; design-tokens.test.ts rejects literal transition durations.
- media rules drop the old green palette fallbacks (var(--text,#30392c) …),
which only ever applied outside the themed scope, and the green-tinted
shadows become neutral.
- the untinted media cover placeholder uses the theme surface instead of a
fixed green gradient, so it no longer flashes green in other themes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Within one stylesheet and one @media context, a property that a later rule
with the same selector sets again can never apply. 74 such declarations are
removed (e.g. the second of three .media-task-tabs looks), along with the
rules they leave empty. A later value an older WebView might reject (dvh,
color-mix, clamp, …) is treated as an enhancement, so the earlier value stays
as its fallback. ui:review scenes are unchanged apart from playback timing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The media styles grew in layers and replaced screens left their rules behind
(old channel switcher, details-based menus, manager tabs/toolbar, match
confirm box, resume cover, settings groups, …). 56 class names had no
reference in the source; rules naming only them are gone, and dead members
of :is()/:where() lists are dropped while the live ones stay. Screenshots of
every ui:review scene match the previous build apart from timing noise.
media-css-classes.test.ts now fails when a media stylesheet names a class the
source never renders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- every typed field is at least --text-title on touch screens, so iOS does not
zoom on focus (guarded by design-tokens.test.ts); the search page too.
- notices float above the phone tab bar; shell and notices use --safe-* vars.
- unknown links land on 首页 instead of the video channel.
- 首页 shows loading and error states for 继续阅读 instead of 还没有在读的书.
- 我的 shows the sync engine's connection state, and 退出登录 asks first.
- SwitchRow names its button from its label and the whole row toggles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a book that cannot open keeps saying why over the stage, with 重试 and 返回;
offline, a book downloaded for offline reading opens from its mirrored record.
- 返回 from a deep link or cold start goes to the shelf instead of leaving.
- keys are heard without focusing anything first; Space/Shift+Space page,
S toggles 朗读, and focused buttons keep Space/Enter.
- 右→左 mirrors taps, swipes, drags and arrow keys.
- the tap that closes a note card no longer also turns the page.
- 目录 opens at the current chapter; the 朗读 button shows 朗读中 while reading.
- the selection toolbar is placed by its measured height and flips above the
selection when it does not fit below.
- staged books fetch the next section while the current one is read, and a
section is never fetched twice at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 在线匹配 no longer opens the confirm sheet on load or after a search; only
pressing a candidate does. Candidate buttons name their candidate.
- 移除在线匹配 asks first and says a series' seasons and episodes lose their
online data too.
- admin pages show MediaScreenError's words (服务正在准备, 不可用, 离线, 无权限)
instead of raw error codes; the back link is named by its destination.
- 扫描全部媒体库 submits every library and reports which ones failed instead of
failing as a whole; scan records show a percentage like the library cards.
- metadata inputs are no longer shrunk to 13px on phones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- enable/pause/default, check interval, save and delete of a source catch
failures and say so; save/delete confirm with a notice.
- 自动追更 shows a load error with 重试 instead of the empty state.
- an empty credential field now keeps the stored value; each configured
credential has its own 清除 button behind a danger confirmation, and the
form is disabled while saving.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
书架:
- sort and filter are read from the URL on every navigation, so back/forward
and the nav's plain 书架 link clear a filter; ?sort= is validated.
- a page past the end clamps to the last page instead of showing 书架还没有书.
- ⋯ is an ActionMenu like every other ⋯, with 从书架拿掉 in the danger colour;
removal reports 从书架拿掉 N 本 or the error, and the confirmation uses the
same words and says the files on disk are untouched.
- the shared Pager replaces the unstyled hand-written one; load errors offer 重试.
书库 and 文件管理:
- 加入书架 reports its result or error; page numbers clamp; the folder listing
no longer follows the book page; the search box follows the URL.
- selection is cleared when the folder or page changes, so 删除 can no longer
act on files that are not on screen; delete confirmations name the files.
- each file action says what it did; both upload buttons report skipped files.
- members opening a 文件管理 link land on the same folder's browsing page, and
admins reloading it are not shown 无权访问 while /auth/me is pending.
- text glyphs (×, ›, +) are icons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- browse move/rename/mkdir/delete/metadata now require an administrator
(403 ADMIN_REQUIRED for members).
- Members may upload, but only add books: unsupported formats are skipped,
zip/cbz are stored as comic archives rather than unpacked, and overwrite is
refused. The new business setting library.memberUpload (default on) turns
member uploads off (403 UPLOAD_DISABLED); GET /library/upload reports
`allowed`.
- The library page shows 上传书籍 to members when uploads are allowed.
- Shelf tests read user_books for the owner only, now that a second user exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
媒体库管理: 新建媒体库 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>