Commit Graph
246 Commits
Author SHA1 Message Date
hectorqinandClaude Opus 5.5 a894bdd198 chore(web): remove CSS for classes nothing renders outside the media styles
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>
2026-10-30 09:49:37 +00:00
hectorqinandClaude Opus 5.5 cdd9abd813 chore(web): remove unused app and media exports
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>
2026-10-30 09:49:37 +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 b997e89059 refactor(web): every icon goes through Icon; one glyph per idea
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>
2026-10-30 07:26:51 +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 6ba3dfc990 style(web): media and reader edges use --safe-*, media focus rings use the token
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>
2026-10-30 04:59:43 +00:00
hectorqinandClaude Opus 5.5 1c16da836d fix(web): keyboard and screen-reader fixes on sources, files, home and the nav
- 书源与追更 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>
2026-10-30 04:56:19 +00:00
hectorqinandClaude Opus 5.5 dee6175425 fix(web): login's server status is an icon with a name, in theme colours
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>
2026-10-30 04:52:38 +00:00
hectorqinandClaude Opus 5.5 b9fa408315 fix(web): slower taps count, long press leaves a selection alone, 朗读 chip says what it does
- 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>
2026-10-30 04:47:59 +00:00
hectorqinandClaude Opus 5.5 84749e5f8c fix(web): 搜书 with no source shows one sentence and the way out
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>
2026-10-30 04:45:21 +00:00
hectorqinandClaude Opus 5.5 fcd9546ed8 fix(web): the video tool row keeps every button on one line
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>
2026-10-30 04:45:21 +00:00
hectorqinandClaude Opus 5.5 7957eb8226 fix(web): media task pages poll less, report batch errors, and surface 自动匹配
- 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>
2026-10-30 04:40:46 +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 0d0e263988 feat(web): one-tap bookmark in the reader's top bar
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>
2026-10-30 04:27:44 +00:00
hectorqinandClaude Opus 5.5 5199f3d4e5 feat(web): a comic's slider moves through the whole book
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>
2026-10-30 04:19:00 +00:00
hectorqinandClaude Opus 5.5 9bd3187179 perf(web): dragging a reader slider re-measures once per frame, brightness never
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>
2026-10-30 04:14:15 +00:00
hectorqinandClaude Opus 5.5 73caebef5d style(web): transitions use the motion tokens; media colours come from the theme
- 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>
2026-10-30 04:10:03 +00:00
hectorqinandClaude Opus 5.5 c93ad465c4 refactor(web): drop media declarations that a later identical selector overrides
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>
2026-10-30 04:04:13 +00:00
hectorqinandClaude Opus 5.5 cdce43f98f refactor(web): remove ~320 lines of media CSS for classes nothing renders
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>
2026-10-30 03:59:39 +00:00
hectorqinandClaude Opus 5.5 d73b987de9 fix(web): shell honours touch and safe-area rules; home and 我的 tell the truth
- 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>
2026-10-30 03:48:46 +00:00
hectorqinandClaude Opus 5.5 23290305c9 fix(web): reader opens offline, explains failures, and pages from the keyboard
- 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>
2026-10-30 03:48:46 +00:00
hectorqinandClaude Opus 5.5 d33eb9971c fix(web): media admin asks before destructive matches and explains errors
- 在线匹配 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>
2026-10-30 03:48:46 +00:00
hectorqinandClaude Opus 5.5 69b63f3529 fix(web): source writes report failures; clearing a credential is explicit
- 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>
2026-10-30 03:48:46 +00:00
hectorqinandClaude Opus 5.5 08c0454192 fix(web): shelf and library follow the URL, report writes, and guard file deletes
书架:
- 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>
2026-10-30 03:48:46 +00:00
hectorqinandClaude Opus 5.5 32595c87a5 feat: file management is admin-only; members upload books behind a setting
- 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>
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 f58a171920 fix(web): show add-to-shelf results as toasts above the open sheet
A failed 加入书架 (e.g. 502 EMPTY_RESULT) set an inline callout behind
the 书源列表 or detail sheet, so nothing appeared. Report failures,
authorization prompts and success with Notyf, which docs/ui.md uses for
source-page feedback and which stays above sheets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:03:43 +00:00
hectorqinandClaude Opus 5.5 7bbfdad698 fix(web): compact sheets size to their content again
cd8f133 gave .sheet-body a zero flex basis so long lists scroll inside
full-height sheets. Compact sheets are content-sized, so the body then
contributed no height: confirmations, prompts and one-field forms showed
only their padding and clipped the text and inputs. Restore an auto basis
for compact sheets; they still shrink and scroll at the height cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 04:08:03 +00:00
hectorqinandClaude Opus 5.5 6cefc1e9bb fix(web): icon-only ⋯ for source row menus; compact sheet for one field
"⋯ 管理" doubled the glyph with a label; row menus elsewhere are a bare
⋯, so keep 管理 as the accessible name only and give the trigger a 44px
target. A form with a single short field opens in a compact sheet, as
docs/ui.md specifies for single-line input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 03:43:35 +00:00
hectorqinandClaude Opus 5.5 59bb4306fc fix(web): source row actions use the ⋯ dropdown instead of expanding
The 我的来源 "管理" button and collapsible extension items (e.g. the
Legado source list) expanded an inline panel that changed the row; the
search options were a hand-built menu without Esc/outside-click handling.
All three now use ActionMenu, as docs/ui.md requires for row actions:
- 我的来源: plugin pages, 基本设置, 设为/取消默认, 暂停/启用.
- Extension items: a form without fields is a menu action (confirmed
  first when it asks), a form with fields opens in a Sheet.
- 搜索选项: 继续搜索 / 重新搜索 keep their explanation via a new
  MenuAction.description, exposed with aria-describedby.
Source rows keep one layout at every width now that nothing expands;
dead expansion and search-menu styles are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 03:27:45 +00:00
hectorqinandClaude Opus 5.5 03bb3e753a fix(web): style the source list sheet's search input
Since the React migration the input itself carries .catalog-source-search,
but the rules still targeted `.catalog-source-search input` from the old
label wrapper. The field fell back to the browser's inset box at 15px,
and the leftover `flex: 1 1 16rem` made it 256px tall in the sheet's
column layout. Style the input directly at 16px and drop the dead
.catalog-source-meta rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 02:55:24 +00:00
hectorqinandClaude Opus 5.5 c8dba91c6d fix(web): sources search picker styles leaked from the card form rules
The source filter's searchable select is rendered inside .sources-card,
whose generic label/input/button rules stacked the search icon above a
second bordered 14px input (iOS zooms on focus) and centred the trigger
text. Its base styles also used media-only aliases (--border, --surface,
--media-selection), so outside media the trigger had no border and the
selected option no highlight. Fall back to the global tokens, scope the
picker's own controls, use a 16px input and hide the native clear button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 02:22:07 +00:00
hectorqinandClaude Opus 5.5 4931516eec fix(web): keep every source failure across streamed search chunks
mergePages replaced the accumulated page with the latest chunk, so only
the most recent chunk's errors were shown. Merge errors by source and
code, as the reader's alternatives panel already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:57:46 +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 4e149c73d7 ci: release only for v* tags or manual runs; CI on main; Android SDK packages
- main is the long-lived development branch: release.yml no longer runs on
  a push to it, only on v* tags and workflow_dispatch. ci.yml now runs on
  every branch push, main included.
- android-actions/setup-android's default package list includes `tools`,
  which current command-line tools no longer carry, so sdkmanager failed;
  install exactly platform-tools, android-35 and build-tools 35.0.1.
- reader-chrome.test.ts left its React roots mounted; a scheduled commit
  could land after jsdom teardown and fail the run with an unhandled
  "Right-hand side of 'instanceof' is not an object" (all tests passing).
  Unmount in afterEach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:19:56 +00:00
hectorqinandClaude Opus 5.5 392148c8da fix(android): shell contract check counts only page-callable methods
check-shell.sh failed on main in three places, none of them real defects:

- Every `fun` at method indentation counted as a bridge method, so the
  Kotlin-only SpeechBridge.interruptForMedia (called by the media bridge,
  not the page) was reported as undeclared. Only methods directly under
  @JavascriptInterface are the page's contract now.
- configuredServerUrl was declared as an optional *property*
  (`configuredServerUrl?: () => string`), which the declaration reader does
  not see. Written as the equivalent optional method, `configuredServerUrl?(): string`.
- The package/directory check stripped only src/main/java/, so every unit
  test under src/test/java/ was reported as misplaced. Any source set's java/
  root is stripped now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:09:32 +00:00
hectorqinandClaude Opus 5.5 4296145721 feat(web): swipe turns pages on phones; swipe between media and shelf tabs
Reader
- The reading surface declared no touch-action, so the browser claimed every
  horizontal drag (pointercancel): a swipe never turned a page on a phone, and
  a rightward one triggered the browser's back gesture and left the book.
  Paged and fixed layouts now leave every move to the gesture layer
  (touch-action: none, on the host and on the paged column in the shadow
  root); scroll mode keeps vertical panning only (pan-y), so it still scrolls
  and no longer navigates away. Horizontal swipes in scroll mode stay inert.

Tabs
- useSwipeTabs (shared/ui): a clearly horizontal touch swipe moves one tab.
  Mouse, short or diagonal moves, scrolls the browser took over, swipes that
  start on a control or a sideways-scrolling row, and active selections are
  ignored; the click the finger lifted over is suppressed. The entering body
  slides in from the swipe side (header and tab row stay put; reduced motion
  disables it).
- Media: the eight catalog tabs are one line across channels — 影视(首页 电影
  剧集)→ 音乐(专辑 歌手 曲目)→ 有声书(有声书 演播者). Library and sort carry
  over within a channel and reset across one.
- Shelf: 全部 · 在读 · 想读 · 已读.

Tests: swipe-tabs (gesture rules, catalog order and params) and the reader's
touch-action rules in reader-layout-css.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:01:46 +00:00
hectorqinandClaude Opus 5.5 1137b0d90d fix(web): paged mode lands on page boundaries; highlights draw and follow reflow; bottom bar stays visible
Paged mode
- Restoring the position after the viewport height changes (address bar,
  keyboard, rotation) wrote max * fraction, which is almost never a column
  boundary: the screen showed half of one page and half of the next. Every
  paged scrollLeft write (restore, TTS follow, revealBlock, search) now goes
  through one page-snapping helper, and a scroll the browser makes on its own
  (focus, selection autoscroll) is snapped back.

Highlights
- localSectionId prefixed `xhtml:` from the format alone, so a windowed book
  whose sections have no prefix saved highlights that matched no chapter and
  were never drawn (and its progress failed to restore). The loaded document's
  own section ids are now the authority.
- Highlight boxes are measured once; font size, margins, mode switch, resize
  or a late image moved the text under them. They are repainted after each.

Mobile bottom bar
- `.reader-route-host` had min-height: 100vh, which on a phone browser is
  taller than the visible viewport while the address bar shows, pushing the
  reader's bottom toolbar under it. Height is 100dvh with a 100vh fallback.
- Notices are bottom-centre, exactly over 朗读/外观; in the reader they now
  stack above the bottom bar instead of swallowing the next tap.

Tap to turn hides the toolbar
- A tap on the outer thirds turns the page and puts a showing toolbar away;
  the middle third still toggles it.

UI review scripts: page-turn-regression waits for login to finish rather than
for the shelf (the app now opens on media); reader-regression shows the
toolbar before pressing 下一章 after tap turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:24:49 +00:00
hectorqin 7dad491c17 fix(web): keep PWA sheets visible 2026-10-26 15:20:06 +08:00
hectorqin ccba6e3d49 fix(web): handle standalone PWA sheet layout 2026-10-26 15:09:30 +08:00
hectorqin a1c8c61102 fix(web): use WebView sheet layout fallback 2026-10-26 14:02:09 +08:00
hectorqin cf14bf3dc5 fix(web): size reader sheets before scrolling 2026-10-26 13:49:46 +08:00
hectorqin 92855d9199 fix(web): make mobile sheet body a bounded scroller 2026-10-26 13:42:45 +08:00
hectorqin cd8f1337c2 fix(web): keep mobile sheets scrollable 2026-10-26 10:00:19 +08:00