Skip to content

fix(reader): 修正註記劃線不隨文字排版重新調整位置的問題,並補上筆記跳轉、註記除錯 log - #24

Merged
Retsomm merged 5 commits into
mainfrom
dev
Aug 2, 2026
Merged

fix(reader): 修正註記劃線不隨文字排版重新調整位置的問題,並補上筆記跳轉、註記除錯 log#24
Retsomm merged 5 commits into
mainfrom
dev

Conversation

@Retsomm

@Retsomm Retsomm commented Aug 2, 2026

Copy link
Copy Markdown
Owner

字體大小變更(pwa/pwa-next)與設定面板觸發的 resize(pwa/pwa-next/renderer)都漏了 重新呼叫 marks-pane 的 pane.render(),導致已存在的劃線標記停留在舊排版位置、跟改版後 的文字對不上;RN 版原本完全沒有這個重繪機制,一併補上。

同時補上「我的筆記」頁點擊個別註記可直接跳轉到書中對應位置(initialCfi),以及
annotations.add/underline 失敗時的除錯 log,方便後續排查標記不顯示的問題。

Summary by CodeRabbit

  • New Features

    • Notes can now open books directly at the referenced passage.
    • Shared links can open a book at a specific reading position.
    • Reading position handling prioritizes linked locations while preserving saved progress.
    • Mobile libraries now include a bundled default book on first launch.
    • Annotation highlights refresh correctly after font-size changes, panel resizing, or layout updates.
  • Bug Fixes

    • Reduced reader errors after closing or replacing book content.
  • Documentation

    • Updated project documentation with web and mobile app status, setup, release workflows, and cloud-sync progress.

字體大小變更(pwa/pwa-next)與設定面板觸發的 resize(pwa/pwa-next/renderer)都漏了
重新呼叫 marks-pane 的 pane.render(),導致已存在的劃線標記停留在舊排版位置、跟改版後
的文字對不上;RN 版原本完全沒有這個重繪機制,一併補上。

同時補上「我的筆記」頁點擊個別註記可直接跳轉到書中對應位置(initialCfi),以及
annotations.add/underline 失敗時的除錯 log,方便後續排查標記不顯示的問題。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
travel-in-time Ready Ready Preview Aug 2, 2026 10:17am

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Retsomm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ef594bcb-9f29-4aca-b729-cf9192503ad9

📥 Commits

Reviewing files that changed from the base of the PR and between 68f3b92 and 14f24a2.

📒 Files selected for processing (3)
  • README.md
  • mobile/app/reader/[id].tsx
  • pwa-next/src/hooks/reader/useReaderEngine.ts
📝 Walkthrough

Walkthrough

The PR documents the multi-project architecture and sync validation status. It adds note links that open books at EPUB CFI locations, suppresses specific destroyed-resource rejections, refreshes annotation panes after layout changes, improves mobile annotation handling, and seeds a default mobile book.

Changes

Reader navigation and mobile initialization

Layer / File(s) Summary
Project and sync documentation
CLOUD_SYNC_PROGRESS.md, README.md
Documents application status, technology stacks, development and release workflows, sync scope, and completed validation scenarios.
CFI note navigation and reader initialization
pwa-next/src/page/Notes.tsx, pwa-next/src/App.tsx, pwa-next/src/page/Reader.tsx, pwa-next/src/hooks/reader/useReaderEngine.ts, pwa-next/src/components/Reader/epubPatches.ts
Adds note links with encoded CFIs, passes optional initial locations through the reader, falls back to saved progress, and suppresses matching replaceCss rejection events.
Annotation rendering and interaction handling
mobile/reader-web/index.ts, pwa-next/src/hooks/reader/useReaderEngine.ts, pwa/src/hooks/reader/useReaderEngine.ts, renderer/src/hooks/reader/useReaderEngine.ts
Re-renders annotation panes after layout changes. The mobile reader tracks selection-mode scroll state and adds touch and relocation diagnostics.
Mobile default-book seeding
mobile/lib/library.ts, mobile/app/(tabs)/index.tsx, mobile/metro.config.js, mobile/package.json
Loads bundled EPUB assets, seeds one default book, persists its metadata, and refreshes the library when the screen gains focus.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Notes
  participant App
  participant Reader
  participant useReaderEngine
  Notes->>App: Navigate to book with encoded CFI
  App->>Reader: Set initialCfi
  Reader->>useReaderEngine: Initialize reader with initialCfi
  useReaderEngine->>useReaderEngine: Display CFI or saved progress
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 標題清楚概括註記重繪、筆記跳轉與除錯日誌等主要變更,且與 PR 內容直接相關。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
CLOUD_SYNC_PROGRESS.md (1)

13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

明確區分離線同步測試與 PWA 冷啟動測試。

Line 14 states that service-worker app-shell caching is untested. Line 353 marks offline reading and synchronization as passed. State whether the passed test used an already loaded application. Keep PWA installation and offline cold-start validation pending if it was not covered.

Also applies to: 350-353

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLOUD_SYNC_PROGRESS.md` around lines 13 - 16, Update the Phase 4 status in
CLOUD_SYNC_PROGRESS.md to clarify whether the passed offline reading and
synchronization tests used an already loaded application; if they did not
validate a fresh PWA launch, keep service-worker installation and offline
cold-start/app-shell caching validation explicitly pending for Phase 5.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pwa-next/src/hooks/reader/useReaderEngine.ts`:
- Around line 632-643: Update the target CFI selection in the display flow
around initialCfi and savedCfi so an empty initialCfi is treated as absent and
savedCfi is selected instead. Preserve the existing one-time initial-CFI
behavior for non-empty values and the fallback handling for display failures.

In `@README.md`:
- Around line 19-20: Update the README privacy statement around “本機優先” so it no
longer claims that no data is uploaded generally. Limit the local-storage claim
to EPUB files and local settings, and explicitly state that pwa-next
synchronizes only lightweight library metadata such as the book list, progress,
bookmarks, and notes, excluding EPUB contents.

---

Nitpick comments:
In `@CLOUD_SYNC_PROGRESS.md`:
- Around line 13-16: Update the Phase 4 status in CLOUD_SYNC_PROGRESS.md to
clarify whether the passed offline reading and synchronization tests used an
already loaded application; if they did not validate a fresh PWA launch, keep
service-worker installation and offline cold-start/app-shell caching validation
explicitly pending for Phase 5.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58412d55-d409-442a-a31e-795a2df06419

📥 Commits

Reviewing files that changed from the base of the PR and between ccc0b8b and dbb853c.

⛔ Files ignored due to path filters (1)
  • mobile/lib/readerHtml.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (10)
  • CLOUD_SYNC_PROGRESS.md
  • README.md
  • mobile/reader-web/index.ts
  • pwa-next/src/App.tsx
  • pwa-next/src/components/Reader/epubPatches.ts
  • pwa-next/src/hooks/reader/useReaderEngine.ts
  • pwa-next/src/page/Notes.tsx
  • pwa-next/src/page/Reader.tsx
  • pwa/src/hooks/reader/useReaderEngine.ts
  • renderer/src/hooks/reader/useReaderEngine.ts

Comment thread pwa-next/src/hooks/reader/useReaderEngine.ts
Comment thread README.md Outdated
Retsomm and others added 2 commits August 2, 2026 16:22
讓測試者不用自己找 epub 匯入,就能直接體驗閱讀、朗讀、劃線等完整功能。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
長按拖曳選字延伸到 .epub-container 邊界時,WebKit/Chromium 會為了讓選取範圍可見強制
捲動該容器(即使是 overflow:hidden),連帶觸發 epub.js 的 scroll → relocated 回報;
既有的「連續卡在同一個 CFI 就強制跳下一章」偵測邏輯誤把這個非預期位移當成翻頁卡死,
而 lastNavDirection 是進入劃線模式前最後一次真實翻頁殘留的全域值,兩者一湊就會誤觸發
gotoTarget() 強制跳章。改為劃線模式中整段跳過這個偵測,並補上 scrollLeft 鎖定機制,
把偵測到的非預期位移即時修正回原位。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
mobile/assets/books/sample-book.epub (1)

1-4359: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider Git LFS for this binary asset.

This EPUB file is committed directly as a binary blob in the main git history. Binary blobs committed this way stay in the repository history permanently, even if the file is later removed or replaced, and they increase clone and CI checkout time for all contributors.

Track this file with Git LFS instead of a plain binary commit. Do this before merge, because migrating already-committed binaries later requires a history rewrite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/assets/books/sample-book.epub` around lines 1 - 4359, Track the
sample-book.epub asset with Git LFS instead of storing it as a regular Git blob.
Add the asset’s pattern to .gitattributes, migrate the existing file into LFS
storage, and ensure the commit contains the LFS pointer rather than the binary
content before merging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mobile/app/`(tabs)/index.tsx:
- Around line 97-100: Update the extraction flow around seedDefaultBook and
handleAddBook so default-book and user-book metadata requests are serialized
rather than overwriting pendingExtractionRef. Queue each extraction and process
requests in order, ensuring every response refreshes metadata for its own book;
alternatively, prevent import while an extraction is active.
- Around line 97-101: Add rejection handling to the detached seedDefaultBook
promise in the tab screen initialization flow, logging the caught error through
the existing app logging mechanism. Preserve the current successful path that
refreshes and extracts metadata, and ensure failures from seedDefaultBook are
handled without unhandled rejections.

In `@mobile/lib/library.ts`:
- Around line 143-169: Update the default-book seeding flow around
DEFAULT_BOOK_SEEDED_KEY and seedDefaultBookPromise so the marker is written only
after the EPUB copy and updateMeta succeed. On failure, clear
seedDefaultBookPromise to allow a later retry, and make retries detect the
persisted sample book record before creating a duplicate when the final marker
write fails.

In `@mobile/reader-web/index.ts`:
- Around line 311-323: Reset annotation state at the start of loadBook alongside
the existing book-specific module-level resets: set annotationModeEnabled to
false and lockedScrollLeft to null, or invoke setAnnotationMode(false) if it
performs both updates. Ensure each newly loaded book starts with normal tap-zone
interaction and no stale scroll lock.
- Around line 658-669: Update the content touchstart and touchmove handlers
around the existing debugLog calls to return immediately when
annotationModeEnabled is false, before reading touch coordinates or logging.
Preserve the current coordinate logging behavior when annotation mode is
enabled, including the passive touchmove listener.

---

Nitpick comments:
In `@mobile/assets/books/sample-book.epub`:
- Around line 1-4359: Track the sample-book.epub asset with Git LFS instead of
storing it as a regular Git blob. Add the asset’s pattern to .gitattributes,
migrate the existing file into LFS storage, and ensure the commit contains the
LFS pointer rather than the binary content before merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 799a9cb4-b6a5-45d9-a6a1-0096f17eec45

📥 Commits

Reviewing files that changed from the base of the PR and between dbb853c and 68f3b92.

⛔ Files ignored due to path filters (2)
  • mobile/lib/readerHtml.generated.ts is excluded by !**/*.generated.*
  • mobile/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • mobile/app/(tabs)/index.tsx
  • mobile/assets/books/sample-book.epub
  • mobile/lib/library.ts
  • mobile/metro.config.js
  • mobile/package.json
  • mobile/reader-web/index.ts

Comment on lines +97 to +100
seedDefaultBook().then((record) => {
if (!record) return;
refresh();
extractMetaFor(record).then(refresh);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Serialize metadata extraction requests.

pendingExtractionRef stores only one request. If the user adds a book before the default-book extraction completes, handleAddBook replaces this pending request. A response can then update metadata for the wrong book.

Put default-book and user-book extraction in one queue, or disable book import until the current extraction completes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/app/`(tabs)/index.tsx around lines 97 - 100, Update the extraction
flow around seedDefaultBook and handleAddBook so default-book and user-book
metadata requests are serialized rather than overwriting pendingExtractionRef.
Queue each extraction and process requests in order, ensuring every response
refreshes metadata for its own book; alternatively, prevent import while an
extraction is active.

Comment on lines +97 to +101
seedDefaultBook().then((record) => {
if (!record) return;
refresh();
extractMetaFor(record).then(refresh);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle seed failures.

seedDefaultBook() can reject during asset download, file copy, or metadata persistence. This detached promise has no rejection handler. Catch the rejection and log it so React Native does not report an unhandled rejection.

Proposed fix
-      seedDefaultBook().then((record) => {
+      void seedDefaultBook()
+        .then(async (record) => {
         if (!record) return;
-        refresh();
-        extractMetaFor(record).then(refresh);
-      });
+        await refresh();
+        await extractMetaFor(record);
+        await refresh();
+      })
+        .catch((err) => console.warn('[library] seedDefaultBook failed', err));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
seedDefaultBook().then((record) => {
if (!record) return;
refresh();
extractMetaFor(record).then(refresh);
});
void seedDefaultBook()
.then(async (record) => {
if (!record) return;
await refresh();
await extractMetaFor(record);
await refresh();
})
.catch((err) => console.warn('[library] seedDefaultBook failed', err));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/app/`(tabs)/index.tsx around lines 97 - 101, Add rejection handling to
the detached seedDefaultBook promise in the tab screen initialization flow,
logging the caught error through the existing app logging mechanism. Preserve
the current successful path that refreshes and extracts metadata, and ensure
failures from seedDefaultBook are handled without unhandled rejections.

Comment thread mobile/lib/library.ts
Comment on lines +143 to +169
const alreadySeeded = await AsyncStorage.getItem(DEFAULT_BOOK_SEEDED_KEY);
if (alreadySeeded) return null;
await AsyncStorage.setItem(DEFAULT_BOOK_SEEDED_KEY, '1');

const asset = Asset.fromModule(require('../assets/books/sample-book.epub'));
await asset.downloadAsync();
if (!asset.localUri) return null;

const dir = booksDir();
if (!dir.exists) dir.create({ intermediates: true });

const id = generateId();
const filename = `${id}.epub`;
const destination = new File(dir, filename);
await new File(asset.localUri).copy(destination);

const record: BookRecord = {
id,
title: '台灣的有機農業',
author: '吳東傑',
filename,
addedAt: Date.now(),
lastOpenedAt: Date.now(),
};

return updateMeta((records) => [[record, ...records], record]);
})();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Persist the seed marker only after seeding succeeds.

Line 145 writes DEFAULT_BOOK_SEEDED_KEY before downloadAsync, copy, and updateMeta. If any later operation fails, later launches return null at line 144 and never install the sample book.

Write the marker after both the EPUB and metadata persist successfully. Reset seedDefaultBookPromise when seeding fails so a later focus event can retry. Make the retry idempotent against the persisted book record if the final marker write fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/lib/library.ts` around lines 143 - 169, Update the default-book
seeding flow around DEFAULT_BOOK_SEEDED_KEY and seedDefaultBookPromise so the
marker is written only after the EPUB copy and updateMeta succeed. On failure,
clear seedDefaultBookPromise to allow a later retry, and make retries detect the
persisted sample book record before creating a duplicate when the final marker
write fails.

Comment on lines +311 to +323
let annotationModeEnabled = false;
// epub.js 的 DefaultViewManager 在 paginated flow 下把 .epub-container 設成
// overflow:hidden,翻頁完全靠 JS 直接改 container.scrollLeft 實現(見 node_modules/epubjs/
// src/managers/default/index.js 的 next()/prev()),不是靠使用者手勢捲動。但 WebKit/
// Chromium 對「文字選取範圍延伸到 overflow:hidden 容器邊界」有個已知行為:即使該容器不接受
// 一般觸控捲動手勢,瀏覽器仍可能為了讓選取範圍保持可見,強制把它 scrollIntoView,直接改動
// container.scrollLeft。這個位移不是經由 turnPage() 發動,但 rendition 的 scroll→relocate
// 回報邏輯(見 DefaultViewManager.onScroll/EVENTS.MANAGERS.SCROLLED)不分青紅皂白,只要
// scrollLeft 變了就當成翻頁回報,畫面看起來就像「框選文字時被強制翻頁」——這跟 tap-zone
// 點擊翻頁是完全不同的路徑,只擋點擊沒辦法擋到這個。
// 劃線模式開啟時鎖住當下的 scrollLeft,之後偵測到「沒有經過 turnPage() 就跑掉」的 scrollLeft
// 位移,立刻鎖回原值,取消掉這個非預期的強制捲動。
let lockedScrollLeft: number | null = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "=== loadBook reset block ==="
rg -n "const loadBook" -A 20 mobile/reader-web/index.ts
echo "=== any existing reset of annotationModeEnabled/lockedScrollLeft ==="
rg -n "annotationModeEnabled|lockedScrollLeft" mobile/reader-web/index.ts
echo "=== RN side: does it always turn off annotation mode before switching books? ==="
rg -n "setAnnotationMode" mobile/app/reader

Repository: Retsomm/TravelInTime

Length of output: 2575


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== mobile/reader-web/index.ts outline around relevant symbols ==="
ast-grep outline mobile/reader-web/index.ts --match setAnnotationMode --view expanded || true
ast-grep outline mobile/reader-web/index.ts --match loadBook --view expanded || true

echo "=== loadBook full function ==="
sed -n '560,790p' mobile/reader-web/index.ts | cat -n

echo "=== annotation mode implementation and event handling ==="
sed -n '308,410p' mobile/reader-web/index.ts | cat -n
sed -n '640,745p' mobile/reader-web/index.ts | cat -n

echo "=== RN send and call sites for engine.postToWebView / setAnnotationMode ==="
rg -n "engine\.postToWebView|postToWebView|setAnnotationMode|loadBook|switching|navigate|onChange|id\]" mobile/app/reader mobile/reader-web -S

echo "=== focused text search for annotationModeEnabled/lockedScrollLeft in mobile/reader-web ==="
rg -n "annotationModeEnabled|lockedScrollLeft|setAnnotationMode|loadBook" mobile/reader-web/index.ts

Repository: Retsomm/TravelInTime

Length of output: 26940


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== mobile/app/reader/[id].tsx relevant state and navigation/load paths ==="
sed -n '1,140p' mobile/app/reader/[id].tsx | cat -n
echo "=== mobile/app/reader/[id].tsx load/navigation callbacks and annotation mode UI ==="
rg -n "onBookChanged|navigate|load|loadBook|setAnnotationMode|annotationMode|toggle|checkbox|annotation" mobile/app/reader/[id].tsx -C 3

echo "=== Web message receiver and loadBook entry point ==="
sed -n '820,900p' mobile/reader-web/index.ts | cat -n

echo "=== focused structural check for loadBook resets ==="
python3 - <<'PY'
from pathlib import Path
p = Path('mobile/reader-web/index.ts')
text = p.read_text()
lines = text.splitlines()
for start, end in [(311, 350), (568, 582)]:
    print(f"--- {start}-{end} ---")
    for i in range(start, min(end, len(lines))+1):
        print(f"{i}\t{lines[i-1]}")
print("loadBook contains 'chapterPageCounts = new Map()'?", "chapterPageCounts = new Map();" in "\n".join(lines[567:581]))
print("loadBook contains 'setAnnotationMode' call?", "setAnnotationMode" in "\n".join(lines[567:581]))
print("any loadBook contains 'lockedScrollLeft' assignment/comment?", any(x in "\n".join(lines[567:581]) for x in ['lockedScrollLeft', 'annotationModeEnabled']))
PY

Repository: Retsomm/TravelInTime

Length of output: 18707


Reset annotation mode state when loadBook starts.

loadBook increments loadGeneration and clears book-specific state, but it leaves annotationModeEnabled and lockedScrollLeft from the previous book. If a book is closed while annotation mode is on, the next book opens with annotationModeEnabled === true; tap zones stay pointer-events: none, and any non-tap-zone scroll is locked back to the old scrollLeft. Reset this state in loadBook at the same time as the other module-level resets, or call setAnnotationMode(false).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/reader-web/index.ts` around lines 311 - 323, Reset annotation state at
the start of loadBook alongside the existing book-specific module-level resets:
set annotationModeEnabled to false and lockedScrollLeft to null, or invoke
setAnnotationMode(false) if it performs both updates. Ensure each newly loaded
book starts with normal tap-zone interaction and no stale scroll lock.

Comment on lines +658 to +669
debugLog(
'[content touchstart]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
});
doc.addEventListener('touchmove', (e: TouchEvent) => {
const t = e.touches[0];
debugLog(
'[content touchmove]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
}, { passive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "const debugLog|function debugLog" -A 20 mobile/reader-web/index.ts

Repository: Retsomm/TravelInTime

Length of output: 1015


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file stats =="
wc -l mobile/reader-web/index.ts

echo "== surrounding scroll/touch handler regions =="
sed -n '330,390p' mobile/reader-web/index.ts
sed -n '630,690p' mobile/reader-web/index.ts

echo "== all annotationModeEnabled references around event setup =="
rg -n "annotationModeEnabled|debugLog|addEventListener\\(" mobile/reader-web/index.ts -C 2

echo "== static behavioral check for early returns in adjacent handlers =="
python3 - <<'PY'
from pathlib import Path
p = Path('mobile/reader-web/index.ts')
text = p.read_text()
for needle in ['addEventListener(\'scroll\'', 'addEventListener(\'touchstart\'', 'addEventListener(\'touchmove\'']:
    idx = text.find(needle)
    print(f'--- {needle}: line {text.count(chr(10),0,idx)+1}')
    start=max(0,idx-400); end=min(len(text), idx+1200)
    block=text[start:end]
    for i,line in enumerate((block+'\n').splitlines(), text.count(chr(10),0,start)+1):
        if i<=50: print(f'{i}: {line}')
PY

Repository: Retsomm/TravelInTime

Length of output: 14193


Gate the content touch handlers on annotationModeEnabled.

debugLog only skips when DEBUG_BRIDGE is false; as long as it is enabled, both handlers build arguments and call post() on every touch. touchmove is high-frequency, and unlike the nearby scroll listener, these handlers no-return when annotation mode is disabled.

⚡ Proposed fix
       doc.addEventListener('touchstart', (e: TouchEvent) => {
         const t = e.touches[0];
+        if (!annotationModeEnabled) return;
         debugLog(
           '[content touchstart]', 'annotationModeEnabled=', annotationModeEnabled,
           'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
         );
       });
       doc.addEventListener('touchmove', (e: TouchEvent) => {
         const t = e.touches[0];
+        if (!annotationModeEnabled) return;
         debugLog(
           '[content touchmove]', 'annotationModeEnabled=', annotationModeEnabled,
           'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
         );
       }, { passive: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
debugLog(
'[content touchstart]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
});
doc.addEventListener('touchmove', (e: TouchEvent) => {
const t = e.touches[0];
debugLog(
'[content touchmove]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
}, { passive: true });
doc.addEventListener('touchstart', (e: TouchEvent) => {
const t = e.touches[0];
if (!annotationModeEnabled) return;
debugLog(
'[content touchstart]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
});
doc.addEventListener('touchmove', (e: TouchEvent) => {
const t = e.touches[0];
if (!annotationModeEnabled) return;
debugLog(
'[content touchmove]', 'annotationModeEnabled=', annotationModeEnabled,
'x=', Math.round(t?.clientX ?? -1), 'y=', Math.round(t?.clientY ?? -1)
);
}, { passive: true });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/reader-web/index.ts` around lines 658 - 669, Update the content
touchstart and touchmove handlers around the existing debugLog calls to return
immediately when annotationModeEnabled is false, before reading touch
coordinates or logging. Preserve the current coordinate logging behavior when
annotation mode is enabled, including the passive touchmove listener.

Retsomm and others added 2 commits August 2, 2026 18:15
…effect 錯誤

將 TTS 輔助函式(noteUserInteraction、clearAllTTSHighlights、clearOtherTTSHighlights、
refreshTTSPageEndOffset、unlockTTSAutoFollow、requestTTSAutoNextPage、cancelScheduledTTSHighlight)
與 rerenderAnnotationPane 移到使用處之前宣告。

睡眠計時器倒數歸零的收尾邏輯(清 interval、清 TTS 高亮、停止朗讀)改為直接在 setInterval
的 tick callback 中處理,取代原本監看 sleepRemaining === 0 的 useEffect,避免在 effect
body 內同步呼叫 setState 觸發連鎖 render。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
toggleListPanel 原本預設分頁是 bookmarks,改為 chapters;README 補充說明本機儲存僅限
EPUB 檔案與本機設定。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Retsomm
Retsomm merged commit d614b00 into main Aug 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant