feat(pdf): paragraph-precise bookmarks
Some checks failed
CI / Windows build (push) Has been cancelled

Add a bookmark tool to the PDF editor. A bookmark anchors to a precise
location: when text is selected it captures the selection's normalized
rect + the start char index in the page text (the true paragraph
anchor); with no selection it falls back to the tapped page + point.

- Bookmark model gains optional normalized anchor rect + charIndex +
  label (all absent from JSON when null, so old sidecars still load).
- Bookmarks persist in the sidecar (scheduleBookmarkUpsert) and load on
  open; a bookmarks panel lists them and tapping one jumps to its page.
  Delete is persisted.

Scoped to the PDF editor (note bookmarks later); scroll-to-anchor is
page-level for now. analyze clean, 391 tests green.
This commit is contained in:
2026-06-25 00:00:16 +08:00
parent c800295c12
commit 1d5ba05bb8
12 changed files with 743 additions and 3 deletions

View File

@@ -566,6 +566,54 @@ abstract class AppLocalizations {
/// **'This removes the anchor and its private scratchpad.'**
String get scratchLinkDeleteBody;
/// No description provided for @toolAddBookmark.
///
/// In en, this message translates to:
/// **'Add bookmark (here or at selection)'**
String get toolAddBookmark;
/// No description provided for @toolBookmarks.
///
/// In en, this message translates to:
/// **'Bookmarks'**
String get toolBookmarks;
/// No description provided for @bookmarksTitle.
///
/// In en, this message translates to:
/// **'Bookmarks'**
String get bookmarksTitle;
/// No description provided for @bookmarksEmpty.
///
/// In en, this message translates to:
/// **'No bookmarks yet.'**
String get bookmarksEmpty;
/// No description provided for @bookmarkDefaultLabel.
///
/// In en, this message translates to:
/// **'Page {page}'**
String bookmarkDefaultLabel(int page);
/// No description provided for @bookmarkPageLabel.
///
/// In en, this message translates to:
/// **'Page {page}'**
String bookmarkPageLabel(int page);
/// No description provided for @bookmarkDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete bookmark?'**
String get bookmarkDeleteTitle;
/// No description provided for @bookmarkDeleteBody.
///
/// In en, this message translates to:
/// **'This removes the saved location.'**
String get bookmarkDeleteBody;
/// No description provided for @failedToOpenPdf.
///
/// In en, this message translates to: