docs(readme): add a Documentation section linking to readest.com/docs (#4551)
Add a Documentation section to the README pointing to the official docs at https://readest.com/docs, with a matching entry in the top navigation and a reference-style link. Also bundle accumulated agent memory updates under apps/readest-app/.claude/memory/. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
<a href="#planned-features">Planned Features</a> •
|
||||
<a href="#screenshots">Screenshots</a> •
|
||||
<a href="#downloads">Downloads</a> •
|
||||
<a href="#documentation">Documentation</a> •
|
||||
<a href="#getting-started">Getting Started</a> •
|
||||
<a href="#troubleshooting">Troubleshooting</a> •
|
||||
<a href="#support">Support</a> •
|
||||
@@ -114,6 +115,12 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion
|
||||
- Linux users can also install [Readest on Flathub][link-flathub].
|
||||
- Web: Visit and use **Readest for Web** at [https://web.readest.com][link-web-readest].
|
||||
|
||||
## Documentation
|
||||
|
||||
Guides, tutorials, and FAQs for installing and using Readest live in the official documentation:
|
||||
|
||||
📖 **[https://readest.com/docs][link-docs]**
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Node.js** and **pnpm** for Next.js development
|
||||
@@ -350,6 +357,7 @@ We would also like to thank the [Web Chinese Fonts Plan](https://chinese-font.ne
|
||||
[link-website]: https://readest.com?utm_source=github&utm_medium=referral&utm_campaign=readme
|
||||
[link-flathub]: https://flathub.org/en/apps/com.bilingify.readest
|
||||
[link-web-readest]: https://web.readest.com
|
||||
[link-docs]: https://readest.com/docs
|
||||
[link-gh-releases]: https://github.com/readest/readest/releases
|
||||
[link-gh-commits]: https://github.com/readest/readest/commits/main
|
||||
[link-gh-pulse]: https://github.com/readest/readest/pulse
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
- [Android Open-with intent flow (#4521)](android-open-with-intent-flow.md) — "Open with"/"Send to" pipeline: `NativeBridgePlugin.kt::handleIntent` → `shared-intent` → `useAppUrlIngress` → `useOpenWithBooks` (VIEW=transient→reader, SEND=library+upload). Telegram fails where file-manager works on TWO axes: cold-start delivery (fixed by #4527, on dev NOT released v0.11.4) + foreign-private-file read (Telegram FileProvider non-persistable grant vs shared-storage FUSE real-path). adb MediaStore VIEW repro tests pipeline but CANNOT reproduce the read axis (MANAGE_EXTERNAL_STORAGE bypasses grant)
|
||||
|
||||
## Feature Notes
|
||||
- [Reference Pages (#672+#4542, PR #4549)](reference-pages-672-4542.md) — 'reference' progressStyle from foliate `pageItem`/`book.pageList` (numeric-max total rule, roman-tail safe); per-book `referencePageCount` via skipGlobal save; verification EPUBs + dev-web synthetic drag-drop import trick; locale-tail rebase-conflict recipe (checkout --ours → re-extract → re-translate)
|
||||
- [OPDS Firefox strict-XML parse (#4479)](opds-firefox-strict-xml-4479.md) — MEK feed has junk after `</feed>`; Firefox DOMParser → `<parsererror>` (silent back-nav), Chrome lenient; `parseOPDSXML` slices root start→last close tag; jsdom mirrors Firefox; wired into page.tsx + validateOPDSURL + feedChecker (latter also #4181 `looksLikeXMLContent` swap)
|
||||
- [OPDS 2.0 JSON search greyed out (#4502)](opds2-json-search-4502.md) — `isSearchLink` ignored templated `application/opds+json` links → `hasSearch` false → disabled navbar input; add `MIME.OPDS2`+`templated`, `expandOPDSSearchTemplate` (foliate `uri-template.js`), handleSearch OPDS2 branch. Gotcha: `resolveURL` mangles `{?query}` braces — expand template BEFORE resolving
|
||||
- [OPDS HTML description (#4503)](opds-html-description-4503.md) — detail-view descriptions showed raw `<p>`/`"` tags; aggregator double-escapes `type="text"` summary + `PublicationView` dumped it into unsanitized `dangerouslySetInnerHTML`; fix = `getOPDSDescriptionHtml` (decode-one-level-iff-fully-escaped, then `sanitizeHtml`)
|
||||
@@ -74,6 +75,9 @@
|
||||
- [Tauri menu append race (#4389)](tauri-menu-append-race-4389.md) — un-awaited `Menu.append()` (async IPC) in `BookshelfItem.tsx` → context-menu items shuffle order every open (native only, invisible in jsdom); fix = single `await Menu.new({ items })` of ordered `MenuItemOptions`; order/inclusion extracted to pure `getBookContextMenuItemIds` for unit testing
|
||||
- [TXT author recognition (#4390)](txt-author-recognition-4390.md) — 【】-titled Chinese web-novels show author missing/garbage; they're TXT→EPUB (title==full filename is the tell, check `txt.ts` not foliate-js); `extractTxtFilenameMetadata` only handled 《》 + greedy header capture grabbed metadata blobs; fix = `parseLabeledAuthor` for any filename + `isPlausibleAuthorName` guard
|
||||
|
||||
## Library Architecture
|
||||
- [Book action platform surfaces](book-actions-platform-surfaces.md) — library context menu is **Tauri-desktop-only** (`hasContextMenu` false on web + iOS/Android); cross-platform book actions go in `BookDetailView`'s icon row. #4543 Goodreads search added both surfaces + a built-in web-search provider for highlighted-text lookup
|
||||
|
||||
## Architecture Notes
|
||||
- foliate-js is a git submodule at `packages/foliate-js/`
|
||||
- Multiview paginator: loads adjacent sections in background, multiple View/Overlayer instances per book
|
||||
@@ -96,3 +100,4 @@
|
||||
- [Never push on every change](feedback_dont_push_every_change.md) — hold pushes during active bug iteration; commit locally only until user confirms or work hits a clean done-state
|
||||
- [No test seams in production code](feedback_no_test_seams_in_prod.md) — production must never import or call `__reset*ForTests`; cross-module test resets belong in the test file's beforeEach/afterEach
|
||||
- [Dependabot transitive fixes](dependabot-pnpm-overrides.md) — pin patched min-version in `pnpm-workspace.yaml` `overrides:` (NOT package.json `pnpm.overrides`, which pnpm 9+ ignores); watch for existing too-low pins; alert#≠issue# so no `Closes #` (PR #4523)
|
||||
- [CI/PR delivery + push keepalive](ci-pr-delivery-and-push.md) — package small PRs from a dirty dev tree via temp-index plumbing (no worktree); slow pre-push hook (~55s full suite) + SOCKS-proxy SSH → idle "Broken pipe", fixed with `ServerAliveInterval`; `--no-verify` safe once the hook already passed (always `git ls-remote` to confirm a push landed)
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
originSessionId: 16f94822-04b0-4be3-a47e-8a2e3cab290a
|
||||
---
|
||||
|
||||
New test tier (branch fix/android-hyphen-selection-1553): `pnpm test:android` → `scripts/test-android.sh` → `vitest.android.config.mts` (node env, serial, retry 1) → `src/__tests__/android/*.android.test.ts`. Helpers in `src/__tests__/android/helpers/`: `adb.ts` (tap/longPress/`motionGesture` = one-shell DOWN/MOVE/UP chain), `cdp.ts` (forward `webview_devtools_remote_<pid>`, node:http discovery with Host header, `CdpPage.evaluate` async-IIFE), `reader.ts` (fixture open + probes). Soft-skips without adb/device/app. Covers the [[android-hyphen-selection-bounds-1553]] cases: prone long-press → app handles, drag repair clamp, tap dismissal, handle-drag extension, mid-paragraph native handles, cross-page corner-dwell auto-turn.
|
||||
New test tier (PR #4545, merged 2026-06-12): `pnpm test:android` → `scripts/test-android.sh` → `vitest.android.config.mts` (node env, serial, retry 1) → `src/__tests__/android/*.android.test.ts`. Helpers in `src/__tests__/android/helpers/`: `adb.ts` (tap/longPress/`motionGesture` = one-shell DOWN/MOVE/UP chain), `cdp.ts` (forward `webview_devtools_remote_<pid>`, node:http discovery with Host header, `CdpPage.evaluate` async-IIFE), `reader.ts` (fixture open + probes). Soft-skips without adb/device/app. Covers the [[android-hyphen-selection-bounds-1553]] cases: prone long-press → app handles, drag repair clamp, tap dismissal, handle-drag extension, mid-paragraph native handles, cross-page corner-dwell auto-turn.
|
||||
|
||||
Design principles (per chrox): discover-don't-assume (find a hyphenated on-screen paragraph at runtime, start in main text via `gotoChapter('chapter\\s*4')`), force hyphenation by injecting `p{hyphens:auto!important;text-align:justify!important}` into section docs (app settings irrelevant), poll-don't-sleep (`waitFor`), fixture `sample-alice.epub` opened TRANSIENTLY via MediaStore VIEW intent.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Key facts:
|
||||
- Auto-hyphens show up as separate ~0.3em rects in `Range.getClientRects()` on hyphenated lines — usable as a generated-hyphen detector.
|
||||
- **JS `removeAllRanges()+addRange()` does NOT hide already-visible touch handles synchronously** — the empty selection must commit through one painted frame (double-rAF in the iframe window) before re-adding; then handles stay hidden for all later JS selection updates.
|
||||
|
||||
Fix (branch fix/android-hyphen-selection-1553): detection utils in `src/utils/sel.ts` (`isHyphenHandleBugProneRange`, `repairJumpedSelectionRange`, `hasTrailingHyphenRectPattern`); gesture-initial anchor capture + touchend sanitize in `useTextSelector` (repair jumped anchor → suppress handles via empty-commit → `makeSelection(handlesSuppressed)`); `SelectionRangeEditor.tsx` renders custom drag handles (reuses `Handle` + extracted `buildRangeFromPoints`/`getHandlePositionsFromRange` in annotatorUtil) for suppressed selections. Gated to Android app + exact bug condition; flipping to always-custom-handles later = drop the proneness gate.
|
||||
Fix (**PR #4545, MERGED 2026-06-12**; worktree cleaned): detection utils in `src/utils/sel.ts` (`isHyphenHandleBugProneRange`, `repairJumpedSelectionRange`, `hasTrailingHyphenRectPattern`); gesture-initial anchor capture + touchend sanitize in `useTextSelector` (repair jumped anchor → suppress handles via empty-commit → `makeSelection(handlesSuppressed)`); `SelectionRangeEditor.tsx` renders custom drag handles (reuses `Handle` + extracted `buildRangeFromPoints`/`getHandlePositionsFromRange` in annotatorUtil) for suppressed selections. Gated to Android app + exact bug condition; flipping to always-custom-handles later = drop the proneness gate.
|
||||
|
||||
Gotcha: `input motionevent DOWN/MOVE/UP` (adb) simulates long-press-drag; `input swipe x y x y 700` simulates plain long-press.
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: book-actions-platform-surfaces
|
||||
description: Where to add a library book action so it reaches every platform (context menu is desktop-only)
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 4efb7b40-cce1-4742-9730-7e93e643d196
|
||||
---
|
||||
|
||||
The library book **context menu** (`BookshelfItem.tsx::bookContextMenuHandler`, native `Menu.new`) only renders where `appService.hasContextMenu` is true — that is **Tauri desktop only** (`nativeAppService.ts`: `!(ios||android)`). It is **false on web AND on iOS/Android**. So a book action added only to the context menu (+ `getBookContextMenuItemIds` in `libraryUtils.ts`) never reaches phone/web users.
|
||||
|
||||
The cross-platform home for book-level actions is the **`BookDetailView` action-icon row** (`src/components/metadata/BookDetailView.tsx`), shown in `BookDetailModal`, reachable on every platform (BookItem tap → details, `Bookshelf.tsx::handleShowDetailsBook`). That row is `flex-nowrap` inside a fixed `h-32` column and already holds up to ~5 icons (Edit/Delete/Download/Upload/Export) — adding more risks phone overflow; keep additions to one small icon.
|
||||
|
||||
**Rule:** desktop-only fast path → context menu; must reach mobile → BookDetailView (or both). Example: the "Search on Goodreads" feature (#4543) added both — `searchGoodreads` context-menu id + a `FaGoodreads` button in BookDetailView, opening `getGoodreadsSearchUrl` via [[open-external-url-helper]]. In-reader highlighted-text Goodreads search is a built-in [[web-search-provider]] entry instead.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: ci-pr-delivery-and-push
|
||||
description: Delivering small PRs from a dirty dev tree without a worktree; the slow pre-push hook + proxy SSH-drop and its keepalive fix
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: c1097233-8b53-422a-98ec-3f0146f32f6b
|
||||
---
|
||||
|
||||
How CI/config PRs get delivered in this repo when `dev` has unrelated uncommitted WIP, and the push gotcha.
|
||||
|
||||
**Packaging a commit onto a fresh PR branch WITHOUT `pnpm worktree:new`** (the worktree script does a full `pnpm install` + `tauri android init` + icon gen — disproportionate for a YAML/package.json-only PR, and you can't `git checkout` a branch in the dev tree because the user's WIP blocks it):
|
||||
1. Edit the target files in the dev tree (only files NOT in the user's WIP set — verify with `git status --short -- <files>`), `git add` just those, commit on `dev` (mirrors how the user wanted the pin committed).
|
||||
2. Re-parent onto `origin/main` (or onto the existing PR-branch tip for a fast-forward add) via a temp index — no checkout, no worktree, dev working tree untouched:
|
||||
```
|
||||
export GIT_INDEX_FILE=$(mktemp); git read-tree <BASE>
|
||||
git update-index --cacheinfo 100644,$(git rev-parse HEAD:<path>),<path> # per changed file
|
||||
TREE=$(git write-tree); unset GIT_INDEX_FILE
|
||||
NEW=$(git log --format=%B -n1 HEAD | git commit-tree $TREE -p <BASE>)
|
||||
git update-ref refs/heads/<branch> $NEW
|
||||
```
|
||||
3. Verify `git diff --stat <BASE>..<branch>` shows ONLY the intended files, then push.
|
||||
|
||||
This is how PR #4547 (pin `android-emulator-runner` + shard `test_web_app`) was built on top of `origin/main` while `dev` carried 49 files of unrelated dictionary/goodreads WIP.
|
||||
|
||||
**Push gotcha (now fixed in `~/.ssh/config`):** `git push` opens the SSH connection BEFORE running the pre-push hook; the husky hook runs the FULL vitest suite (~55s, 5271 tests) + format + lint. The user pushes through a SOCKS proxy (`nc -x 127.0.0.1:8119` → `ssh.github.com:443`), so the idle connection got dropped during the hook → "Broken pipe", ref never transferred (remote stayed at old SHA — always `git ls-remote` to confirm). Fix added: `ServerAliveInterval 15` + `ServerAliveCountMax 60` under `Host github.com`. Also: **`--no-verify` is safe once the hook has already passed** on the same tree — re-running it just re-opens the idle window. See also [[feedback_dont_push_every_change]], [[feedback_use_worktree]].
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: reference-pages-672-4542
|
||||
description: "Reference Pages progress style (#672 +"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 2a51b785-c6db-4bab-86f1-4a9b086a0e48
|
||||
---
|
||||
|
||||
PR #4549 (2026-06-12) merged #4542 (manual page count) into #672 (page-list/page-map): `progressStyle: 'reference'` shows physical-book pages in `ProgressBar` + `DesktopFooterBar`.
|
||||
|
||||
- **foliate-js already did the hard part**: `book.pageList` (EPUB3 nav page-list at `epub.js` parseNav; EPUB2 NCX `pageList` — only parsed when there's no navigable nav-doc TOC) and `view.js #pageProgress` emits `detail.pageItem` on every relocate. Readest never consumed it before. Adobe `page-map.xml` is NOT parsed, but books that ship it (Count Zero) also carry the NCX pageList, so it works anyway. Gap: EPUB3 nav-with-TOC-but-no-page-list never falls back to NCX pageList.
|
||||
- **Total = highest numeric label**, not last entry (a trailing roman-numeral index page like "XII" after p553 would corrupt it — reported in #672 comments). All-roman page lists fall back to entry count. Logic in `getReferencePageInfo` (`utils/progress.ts`).
|
||||
- **Per-book-only viewSettings field**: `referencePageCount` saved with `saveViewSettings(..., skipGlobal=true)` so a physical page count never leaks into `globalViewSettings` even when the user runs global settings. The merge `{...global, ...perBook}` keeps it.
|
||||
- **Verification EPUBs** (issue #672 comments, in /tmp/issue672 while it lasts): Caleb's Crossing = EPUB3 nav page-list (419 pp); Count Zero = EPUB2 NCX pageList + page-map.xml (346 pp; `Text/c2.html` starts at `name="22"` — good exact-match oracle).
|
||||
- **Web-import injection trick for dev-web e2e**: stage the epub in `public/`, then dispatch a synthetic `DragEvent('drop')` with a real `DataTransfer` (files added via `dt.items.add(new File(...))`) on `.library-page` — `useDragDropImport` takes it from there. The chrome-MCP `javascript_tool` has NO top-level await ("await only valid in async functions") and collects returned promises — use an async IIFE writing to `window.__result` and poll.
|
||||
- **Locale-file rebase conflicts** (every feature PR appends keys to all 33 translation.json tails): don't hand-merge — `git checkout --ours -- public/locales`, re-run `pnpm i18n:extract`, re-run the translation script, `git add`, `git rebase --continue`.
|
||||
Reference in New Issue
Block a user