fix(ui): refine reader side panels and their empty states (#4361)
* docs(agent): add agent notes for cache, reading-ruler, foliate touch Add project-memory notes and index entries: - manage-cache-ios-layout: iOS container layout and what Manage Cache clears - reading-ruler-line-aware: line/column-aware reading ruler internals - foliate-touch-listener-capture-phase: capture-phase gesture suppression Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reader): pad sidebar and notebook for the device status bar (#4089) Top-anchored slide-in panels (sidebar, notebook) only applied status-bar top padding when isFullHeightInMobile was true. On a tablet/desktop (isMobile === false) that gate collapsed the padding to 0, so a visible system status bar overlapped the panel's top toolbar and made its icons inaccessible. Extract the inset math into getPanelTopInset() and gate it on (!isMobile || isFullHeightInMobile) so non-mobile panels clear the status bar like the reader header, while a partial-height mobile bottom sheet (which doesn't reach the top of the screen) stays flush. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reader): keep footer bar clear of the pinned sidebar On a mobile tablet in portrait, forceMobileLayout renders the footer bar with position: fixed, anchored to the viewport, so left-0 w-full spans the whole window and slides under a pinned sidebar — the progress / font / TTS controls end up obscured. Anchor the footer inside the book's grid cell (position: absolute) when the sidebar is pinned, mirroring the header bar. The flex layout already offsets the grid cell by the sidebar's real rendered width, which honors the sidebar's min-w-60 floor and 45% cap that a stored-width offset would miss. The slide-up panels are absolute within the footer container, so they shift and narrow with it and their animation is unchanged. The switch only happens when the sidebar is pinned, so phone (< 640px) and unpinned tablet-portrait class names stay identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ui): refine reader side panels and their empty states Closes #4089 Add a shared EmptyState component (large muted icon, title, and an optional hint or action) and use it for the empty annotations, bookmarks, and notes panels in the sidebar and notebook, replacing the ad-hoc "No … yet" placeholders. Polish the surrounding chrome: switch the bookmark toggler to the Ri icon set with responsive sizing, crop the HighlighterIcon viewBox to its artwork to remove the asymmetric bottom padding, and tune mobile sizing and spacing across the panel headers, tab navigation, and footer nav bar. Translate the new empty-state strings (No Notes, No Annotations, No Bookmarks, and their hints/action) across all 33 locales and drop the obsolete "No … yet" keys. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
## Paginator Scroll Knowledge
|
||||
- [Issue #4112 scroll-anchoring](issue-4112-scroll-anchoring.md) — RESOLVED (PR #4349). Scroll-anchoring suppressed at scrollTop 0 when prepending a section in scrolled mode; fix patterns (prepend compensation, eager backward preload, no-blank nav) + test & dev-server gotchas
|
||||
- [Reading ruler line/column-aware](reading-ruler-line-aware.md) — ruler snaps to real lines; multi-column band spans one column; Range.getClientRects() returns tall block boxes that must be dropped; iframe frame-offset mapping; synthetic-key throttling
|
||||
|
||||
## Critical Files (Most Bug-Prone)
|
||||
- `src/utils/style.ts` - Central EPUB CSS transformation hub (14+ bug fixes)
|
||||
@@ -20,6 +21,7 @@
|
||||
- `src/app/reader/components/annotator/Annotator.tsx` - Annotation lifecycle
|
||||
|
||||
## Feature Notes
|
||||
- [Manage Cache + iOS container layout](manage-cache-ios-layout.md) — `'Cache'` base = `Library/Caches/<bundle>` only (not all of Caches); iOS `Documents/Inbox` cleared too; WebKit cache + tmp out of reach; never touch App Support
|
||||
- [D-pad Navigation](dpad-navigation.md) — Android TV remote / keyboard arrow navigation design, key files, and pitfalls
|
||||
- [Cloudflare Workers WebSocket](cloudflare-workers-websocket.md) — use fetch() Upgrade pattern (not `ws` npm); CF delivers binary frames as Blob (must serialize async decodes)
|
||||
- [Share-a-Book Feature (in progress)](share-feature.md) — locked decisions for the /s/{token} share-link feature; plan at ~/.claude/plans/ok-we-will-learn-cosmic-acorn.md
|
||||
@@ -37,6 +39,7 @@
|
||||
- TTS uses independent section tracking (`#ttsSectionIndex`) decoupled from view
|
||||
- Safe area insets flow: Native plugin -> useSafeAreaInsets hook -> component styles
|
||||
- Dropdown menus use `DropdownContext` (not blur-based) for screen reader compat
|
||||
- [Foliate touch-listener capture phase](foliate-touch-listener-capture-phase.md) — to suppress reader gestures from the app, use `{capture:true}`; the paginator registers bubble-phase doc listeners first (during `view.open()`)
|
||||
|
||||
## Workflow
|
||||
- [Test file filter](feedback_test_file_filter.md) — use `pnpm test <path>` without `--` to run a single file
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: foliate-touch-listener-capture-phase
|
||||
description: "To intercept/suppress reader touch gestures from the app, use capture-phase listeners — foliate-js's paginator registers bubble-phase doc listeners first"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 4b0bfcd2-a4ed-4b3c-99c2-b3c37ef7c530
|
||||
---
|
||||
|
||||
There are **three** independent touch-listener registrants on the foliate iframe `doc`:
|
||||
1. `FoliateViewer.tsx` (~line 326) — passive forwarders that only `postMessage`.
|
||||
2. `Annotator.tsx` (~line 332) — non-passive, drive text selection.
|
||||
3. **foliate-js's own paginator** (`packages/foliate-js/paginator.js:1034`) — non-passive, **bubble-phase**, registered during `view.open()` (so *before* any app-level `load` handler). It can `preventDefault`, set `#touchScrolled`, `scrollBy`.
|
||||
|
||||
Consequence: a bubble-phase app listener registered "before the existing FoliateViewer listeners" **cannot** `stopImmediatePropagation` the paginator — the paginator already ran. Registration order only controls listeners within the same phase, and the paginator's are earlier regardless.
|
||||
|
||||
**Fix pattern:** register with `{ capture: true, passive: false }`. Capture-phase listeners on `doc` fire before all bubble-phase listeners when the event target is a descendant, so capture-phase `stopImmediatePropagation()` suppresses paginator + Annotator + FoliateViewer handlers alike. Scrolled mode also needs `preventDefault` from the first armed move (the paginator early-returns on `scrolled`, so native container scroll is what moves content).
|
||||
|
||||
Verified end-to-end for the [[brightness-swipe-gesture]] feature (test asserts a bubble-phase paginator stand-in never fires after a capture-phase `stopImmediatePropagation`). Both Codex and a Claude subagent independently confirmed against `paginator.js` during the /autoplan review.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: manage-cache-ios-layout
|
||||
description: "iOS app container layout and what the Manage Cache feature can/can't clear"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 3512356b-a453-42d3-99f6-1ca43d06dd1e
|
||||
---
|
||||
|
||||
Manage Cache feature: `src/utils/cache.ts` (helpers `getCacheEntries`/`getCacheStats`/`clearCacheEntries` over `CacheSource[]`), `src/app/library/components/CacheManagerWindow.tsx` (singleton dialog, `setCacheManagerDialogVisible`, `getCacheSources()` composes the source list), menu item in `SettingsMenu.tsx` Advanced Settings, mounted in `library/page.tsx`.
|
||||
|
||||
Scope: **native mobile apps only** (gated on `appService?.isMobileApp`; hidden on desktop + web). Sources cleared: iOS → Cache + Temp + `Documents/Inbox`; Android → Cache + Temp.
|
||||
|
||||
On-device analysis (dev build `com.bilingify.readest`, pulled via `xcrun devicectl device copy from --domain-type appDataContainer`):
|
||||
|
||||
- The `'Cache'` base = Tauri `appCacheDir()` = **`Library/Caches/com.bilingify.readest`** only — NOT all of `Library/Caches`. On the test device this held ~272 MB: a 249 MB duplicate dictionary (`concise-enhanced.mdx`, canonical copy lives in `App Support/Readest/Dictionaries/<id>/`), ~23 MB duplicate import-staged epubs (canonical in `App Support/Readest/Books/<hash>/`), the `search/` results cache, plus tiny system scratch. All safe to clear — every large item is an orphaned import/download staging duplicate.
|
||||
- iOS open-in leftovers: **`Documents/Inbox`** (resolved via `documentDir()` + join `Inbox`, scanned/cleared with base `'None'` + absolute paths). Already-imported books linger here. The feature now clears it on iOS. (`tmp/<bundle>-Inbox` also exists but was empty.)
|
||||
- NOT reachable by the `'Cache'` base: `Library/Caches/WebKit` (~173 MB, WKWebView disk cache — needs native `WKWebsiteDataStore.removeData`) and `tmp/` blob scratch (~59 MB, maps to `'Temp'` base, not currently cleared). These are the bigger "free up space" wins if the feature is ever expanded.
|
||||
- Never clear `Library/Application Support/com.bilingify.readest/Readest` — the real Books/Dictionaries/Fonts/DB (~1.7 GB).
|
||||
|
||||
Root-cause follow-up worth doing: the import pipeline leaves staging copies in the cache root and `Documents/Inbox` instead of deleting them after import.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: reading-ruler-line-aware
|
||||
description: "Line-aware/column-aware reading ruler — geometry pipeline, key files, and the Range.getClientRects block-box gotcha"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: f55c9971-c85f-44af-82c3-822e3bbd1129
|
||||
---
|
||||
|
||||
Reading ruler (`src/app/reader/components/ReadingRuler.tsx`, pure logic in
|
||||
`src/app/reader/utils/readingRuler.ts`) snaps to real rendered text lines instead of
|
||||
stepping by a fixed arithmetic height. MERGED to main via PR #4358 (squash); the
|
||||
`feat/line-aware-reading-ruler` branch + worktree are gone. Spec/plan under
|
||||
`apps/readest-app/docs/superpowers/`.
|
||||
|
||||
Geometry pipeline:
|
||||
- Line geometry comes from `progress.range.getClientRects()` (the foliate relocate range
|
||||
spans first-to-last visible text). Single column + vertical mode → `buildLineBoxes` +
|
||||
`snapReadingRulerToLines` (full-width band). Multi-column horizontal → `buildReadingRulerColumns`
|
||||
+ `snapReadingRulerColumns`, band spans one column, dims the rest incl. the other column.
|
||||
- The snap functions return the real line-block extent `{start,end}`; the band is sized
|
||||
dynamically to that block + symmetric padding (`calculateReadingRulerPadding` =
|
||||
`round(fontSize*lineHeight*0.4)`) so padding is equal all around. `currentPosition` stays
|
||||
the band center % (drag/persistence); `bandSize` state holds the dynamic thickness, falling
|
||||
back to `baseRulerSize + 2*padding` for scrolled/fixed-layout/drag/unmeasured.
|
||||
- Multi-column detection: `view.renderer.columnCount` (already on the `Renderer` type).
|
||||
- Snapping works in scrolled mode too (`supportsLineSnap` no longer excludes scrolled).
|
||||
Single-column geometry goes through `buildVisibleLineBoxes` → frame-offset mapping, which
|
||||
is REQUIRED in scrolled mode (the visible section iframe is offset vertically by the scroll;
|
||||
there are several stacked section iframes, frame tops far negative). Auto-move stays disabled
|
||||
in scrolled mode (no jump on scroll); the band derives from the saved position on mount.
|
||||
- GOTCHA: in scrolled mode `progress.range` (the relocate range) covers only PART of the
|
||||
viewport, so using it would make the band hit a false end mid-view and scroll early (skipping
|
||||
lines). Scrolled mode instead builds boxes from the visible section(s) directly
|
||||
(`buildScrolledLineBoxes`: walk `view.renderer.getContents()`, `selectNodeContents(body)`,
|
||||
frame-offset map, filter to viewport). At a view edge a tap sets `pendingScrollAlignRef`; the
|
||||
view scrolls and the next relocate realigns the band to the first (forward) / last (backward)
|
||||
group. `filterVisibleLineBoxes` (≥0.5 visible) keeps the mount/realign placement on-screen.
|
||||
- GOTCHA: do NOT call `renderer.scrollBy()` yourself to advance the band in scrolled mode — a
|
||||
manual scroll that crosses a section boundary fires the relocate MID-relayout, so
|
||||
`getContents()` frame offsets are stale and `buildScrolledLineBoxes` returns garbage (huge
|
||||
blocks spanning gaps) → the band loops in place. Instead, when the next block can't be
|
||||
centered, return false so foliate page-scrolls (its relocate fires AFTER layout settles) and
|
||||
realign via `pendingScrollAlignRef`. Band is always centered on its block (no center clamp).
|
||||
- Band height is capped at `calculateReadingRulerSize(lines + 1, …)` so a tall element (e.g. a
|
||||
full-page image) inside the snapped block can't expand the band to cover the whole image.
|
||||
- **Coordinate mapping**: paginated multi-column pages shift the iframe far off-screen
|
||||
horizontally (`frameRect.left` was -4773 in testing); map iframe-content rects to overlay
|
||||
coords with the iframe `frameElement` offset (`mapRangeRectsToOverlay`), not just
|
||||
`rect.top - containerRect.top`.
|
||||
|
||||
**GOTCHA (caused a paragraph-skip bug):** `Range.getClientRects()` aggregates the border
|
||||
boxes of every *fully-enclosed element*, so multi-line `<p>`/container blocks return rects
|
||||
far taller than a text line (e.g. h=410 spanning a whole paragraph) alongside the line
|
||||
rects. An overlap-based line merge chains those into one giant "line", making the snap skip
|
||||
an entire paragraph — worse with more paragraphs per column. Fix: `dropBlockRects()` discards
|
||||
rects whose cross-axis thickness > 1.8× the median line thickness before clustering.
|
||||
|
||||
**Vertical key mapping**: in vertical writing mode only Up/Down arrows move the ruler;
|
||||
Left/Right turn pages (`isReadingRulerMoveKey(side, isVertical)` in `readingRuler.ts`, gating
|
||||
`moveReadingRuler` in `useBookShortcuts.ts`). Taps always move the ruler regardless of the
|
||||
tapped side (the restriction is keyboard-only; the tap path in `usePagination.ts` is untouched).
|
||||
|
||||
**Scrolled vertical-rl gotcha**: scrolled vertical scrolls HORIZONTALLY (section iframes stack
|
||||
along x; all share top/bottom). `progress.range` covers only the left ~30% of the visible width,
|
||||
so building boxes from it makes the edge realign land mid-view. `buildScrolledLineBoxes` is now
|
||||
vertical-aware (horizontal frame-visibility filter `frame.right<=cont.left || frame.left>=cont.right`,
|
||||
`buildLineBoxes(mapped, isVertical, …)`) and the scrolled edge-scroll path (`buildScrolledLineBoxes`
|
||||
choice, centerable check) no longer excludes vertical (`scrolled = !!viewSettings.scrolled`, was
|
||||
`&& !isVertical`). At the last column, advancing scrolls the view forward and snaps the band to the
|
||||
first (rightmost) column; backward snaps to the last (leftmost). PAGINATED vertical already paged at
|
||||
the boundary (snap returns null → handler returns false → keyboard `viewPagination` → `view.next()`;
|
||||
auto-move effect places the band on the new page's first column).
|
||||
|
||||
Verified live with Chrome MCP by reconstructing the column pipeline in-page and dispatching
|
||||
ArrowRight/ArrowLeft. Note: rapid synthetic key events in a tight loop get coalesced/throttled
|
||||
by the reader's nav handling — drive one press per tool call with real time between. To inspect
|
||||
logged objects (read_console_messages shows "Object"), monkey-patch console.log in-page to push
|
||||
JSON.stringify'd args into a window array, then read that array.
|
||||
@@ -31,6 +31,8 @@ style={{
|
||||
}}
|
||||
```
|
||||
|
||||
For top-anchored slide-in panels (sidebar, notebook), use `getPanelTopInset()` from `src/utils/insets.ts`. It clears the status bar on tablet/desktop and full-height mobile sheets, but stays flush for a partial-height mobile bottom sheet (which doesn't reach the top of the screen). Gating only on `isFullHeightInMobile` is wrong — a non-mobile panel is also top-anchored and would let the status bar obscure its toolbar.
|
||||
|
||||
### Bottom Inset Rules
|
||||
|
||||
For UI elements anchored to the **bottom** of the screen (footer bars, controls, progress indicators), use `gridInsets.bottom * 0.33` as padding — a fraction of the full inset since bottom bars don't need as much clearance as the home indicator area:
|
||||
|
||||
@@ -1802,9 +1802,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "استيراد التمييزات والملاحظات المصدّرة من تطبيق قراءة آخر.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "ملف تصدير Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "لا توجد ملاحظات بعد",
|
||||
"No annotation yet": "لا توجد تعليقات بعد",
|
||||
"No bookmark yet": "لا توجد إشارات مرجعية بعد",
|
||||
"Importing...": "جاري الاستيراد...",
|
||||
"Swipe for Brightness": "اسحب لضبط السطوع",
|
||||
"Slide along the left edge": "اسحب على طول الحافة اليسرى",
|
||||
@@ -1824,5 +1821,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "سيؤدي هذا إلى حذف جميع الملفات المخزنة مؤقتًا. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"Confirm Clear": "تأكيد المسح",
|
||||
"Clearing...": "جارٍ المسح…",
|
||||
"Clear Cache": "مسح ذاكرة التخزين المؤقت"
|
||||
"Clear Cache": "مسح ذاكرة التخزين المؤقت",
|
||||
"No Notes": "لا توجد ملاحظات",
|
||||
"Capture an idea as you read": "دوّن فكرة أثناء القراءة",
|
||||
"No Annotations": "لا توجد تعليقات توضيحية",
|
||||
"No Bookmarks": "لا توجد إشارات مرجعية",
|
||||
"Select some text to highlight": "حدّد نصًا لتمييزه",
|
||||
"Bookmark This Page": "أضف إشارة مرجعية لهذه الصفحة"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "অন্য একটি রিডিং অ্যাপ থেকে রপ্তানি করা হাইলাইট ও নোট আমদানি করুন।",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader রপ্তানি ফাইল (.mrexpt)",
|
||||
"No note yet": "এখনও কোনো নোট নেই",
|
||||
"No annotation yet": "এখনও কোনো টীকা নেই",
|
||||
"No bookmark yet": "এখনও কোনো বুকমার্ক নেই",
|
||||
"Importing...": "আমদানি হচ্ছে...",
|
||||
"Swipe for Brightness": "উজ্জ্বলতার জন্য সোয়াইপ করুন",
|
||||
"Slide along the left edge": "বাম প্রান্ত বরাবর স্লাইড করুন",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "এটি সমস্ত ক্যাশ করা ফাইল মুছে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না।",
|
||||
"Confirm Clear": "সাফ করা নিশ্চিত করুন",
|
||||
"Clearing...": "সাফ করা হচ্ছে…",
|
||||
"Clear Cache": "ক্যাশ সাফ করুন"
|
||||
"Clear Cache": "ক্যাশ সাফ করুন",
|
||||
"No Notes": "কোনো নোট নেই",
|
||||
"Capture an idea as you read": "পড়ার সময় একটি ভাবনা টুকে রাখুন",
|
||||
"No Annotations": "কোনো টীকা নেই",
|
||||
"No Bookmarks": "কোনো বুকমার্ক নেই",
|
||||
"Select some text to highlight": "হাইলাইট করতে কিছু লেখা নির্বাচন করুন",
|
||||
"Bookmark This Page": "এই পৃষ্ঠা বুকমার্ক করুন"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "ཀློག་འདོན་ཉེར་སྤྱོད་གཞན་ཞིག་ནས་ཕྱིར་འདོན་བྱས་པའི་གཙོ་གནད་དང་ཟིན་བྲིས་ནང་འདྲེན་བྱེད།",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader ཕྱིར་འདོན་ཡིག་ཆ (.mrexpt)",
|
||||
"No note yet": "ད་དུང་ཟིན་བྲིས་མེད།",
|
||||
"No annotation yet": "ད་དུང་མཆན་འགྲེལ་མེད།",
|
||||
"No bookmark yet": "ད་དུང་དཔེ་རྟགས་མེད།",
|
||||
"Importing...": "ནང་འདྲེན་བྱེད་བཞིན་པ...",
|
||||
"Swipe for Brightness": "འོད་གདངས་ཆེད་འཐེན།",
|
||||
"Slide along the left edge": "གཡོན་ངོས་ཟུར་ནས་འཐེན།",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "འདིས་གསོག་ཉར་གྱི་ཡིག་ཆ་ཚང་མ་བསུབ་ངེས་ཡིན། འདི་སྔར་བཞིན་བཟོ་མི་ཐུབ།",
|
||||
"Confirm Clear": "བསལ་བ་གཏན་འཁེལ།",
|
||||
"Clearing...": "བསལ་བཞིན་པ།...",
|
||||
"Clear Cache": "གསོག་ཉར་བསལ་བ།"
|
||||
"Clear Cache": "གསོག་ཉར་བསལ་བ།",
|
||||
"No Notes": "ཟིན་བྲིས་མེད།",
|
||||
"Capture an idea as you read": "ཀློག་སྐབས་བསམ་ཚུལ་ཞིག་ཟིན་བྲིས་གྱིས།",
|
||||
"No Annotations": "མཆན་མེད།",
|
||||
"No Bookmarks": "དཔེ་རྟགས་མེད།",
|
||||
"Select some text to highlight": "འོག་ཐིག་གཏོང་བར་ཡི་གེ་འདེམས་རོགས།",
|
||||
"Bookmark This Page": "ཤོག་ངོས་འདིར་དཔེ་རྟགས་འགོད།"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importieren Sie Hervorhebungen und Notizen, die aus einer anderen Leseanwendung exportiert wurden.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader-Exportdatei (.mrexpt)",
|
||||
"No note yet": "Noch keine Notiz",
|
||||
"No annotation yet": "Noch keine Anmerkung",
|
||||
"No bookmark yet": "Noch kein Lesezeichen",
|
||||
"Importing...": "Wird importiert...",
|
||||
"Swipe for Brightness": "Wischen für Helligkeit",
|
||||
"Slide along the left edge": "Am linken Rand entlang wischen",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Dadurch werden alle zwischengespeicherten Dateien gelöscht. Dies kann nicht rückgängig gemacht werden.",
|
||||
"Confirm Clear": "Leeren bestätigen",
|
||||
"Clearing...": "Wird geleert …",
|
||||
"Clear Cache": "Cache leeren"
|
||||
"Clear Cache": "Cache leeren",
|
||||
"No Notes": "Keine Notizen",
|
||||
"Capture an idea as you read": "Halte eine Idee beim Lesen fest",
|
||||
"No Annotations": "Keine Anmerkungen",
|
||||
"No Bookmarks": "Keine Lesezeichen",
|
||||
"Select some text to highlight": "Wähle Text zum Hervorheben aus",
|
||||
"Bookmark This Page": "Diese Seite mit Lesezeichen versehen"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Εισαγωγή επισημάνσεων και σημειώσεων που εξήχθησαν από άλλη εφαρμογή ανάγνωσης.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Αρχείο εξαγωγής Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Δεν υπάρχει ακόμη σημείωση",
|
||||
"No annotation yet": "Δεν υπάρχει ακόμη σχολιασμός",
|
||||
"No bookmark yet": "Δεν υπάρχει ακόμη σελιδοδείκτης",
|
||||
"Importing...": "Εισαγωγή...",
|
||||
"Swipe for Brightness": "Σάρωση για φωτεινότητα",
|
||||
"Slide along the left edge": "Σύρετε κατά μήκος της αριστερής άκρης",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Αυτό θα διαγράψει όλα τα προσωρινά αποθηκευμένα αρχεία. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.",
|
||||
"Confirm Clear": "Επιβεβαίωση εκκαθάρισης",
|
||||
"Clearing...": "Εκκαθάριση…",
|
||||
"Clear Cache": "Εκκαθάριση προσωρινής μνήμης"
|
||||
"Clear Cache": "Εκκαθάριση προσωρινής μνήμης",
|
||||
"No Notes": "Καμία σημείωση",
|
||||
"Capture an idea as you read": "Καταγράψτε μια ιδέα καθώς διαβάζετε",
|
||||
"No Annotations": "Κανένας σχολιασμός",
|
||||
"No Bookmarks": "Κανένας σελιδοδείκτης",
|
||||
"Select some text to highlight": "Επιλέξτε κείμενο για επισήμανση",
|
||||
"Bookmark This Page": "Προσθήκη σελιδοδείκτη σε αυτή τη σελίδα"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importa resaltados y notas exportados desde otra aplicación de lectura.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Archivo de exportación de Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Aún no hay notas",
|
||||
"No annotation yet": "Aún no hay anotaciones",
|
||||
"No bookmark yet": "Aún no hay marcadores",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para el brillo",
|
||||
"Slide along the left edge": "Desliza por el borde izquierdo",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Esto eliminará todos los archivos en caché. Esta acción no se puede deshacer.",
|
||||
"Confirm Clear": "Confirmar borrado",
|
||||
"Clearing...": "Borrando…",
|
||||
"Clear Cache": "Borrar caché"
|
||||
"Clear Cache": "Borrar caché",
|
||||
"No Notes": "Sin notas",
|
||||
"Capture an idea as you read": "Captura una idea mientras lees",
|
||||
"No Annotations": "Sin anotaciones",
|
||||
"No Bookmarks": "Sin marcadores",
|
||||
"Select some text to highlight": "Selecciona texto para resaltar",
|
||||
"Bookmark This Page": "Añadir esta página a marcadores"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "وارد کردن هایلایتها و یادداشتهای صادرشده از یک برنامهی مطالعهی دیگر.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "فایل خروجی Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "هنوز یادداشتی وجود ندارد",
|
||||
"No annotation yet": "هنوز یادداشتی وجود ندارد",
|
||||
"No bookmark yet": "هنوز نشانکی وجود ندارد",
|
||||
"Importing...": "در حال وارد کردن...",
|
||||
"Swipe for Brightness": "کشیدن برای روشنایی",
|
||||
"Slide along the left edge": "در امتداد لبه چپ بکشید",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "این کار همهٔ فایلهای ذخیرهشده در حافظهٔ پنهان را حذف میکند. این عمل قابل بازگشت نیست.",
|
||||
"Confirm Clear": "تأیید پاک کردن",
|
||||
"Clearing...": "در حال پاک کردن…",
|
||||
"Clear Cache": "پاک کردن حافظهٔ پنهان"
|
||||
"Clear Cache": "پاک کردن حافظهٔ پنهان",
|
||||
"No Notes": "هیچ یادداشتی نیست",
|
||||
"Capture an idea as you read": "هنگام خواندن، ایدهای را ثبت کنید",
|
||||
"No Annotations": "هیچ حاشیهنویسیای نیست",
|
||||
"No Bookmarks": "هیچ نشانکی نیست",
|
||||
"Select some text to highlight": "برای هایلایت کردن، متنی را انتخاب کنید",
|
||||
"Bookmark This Page": "افزودن این صفحه به نشانکها"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importez des surlignages et des notes exportés depuis une autre application de lecture.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Fichier d'exportation Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Aucune note pour le moment",
|
||||
"No annotation yet": "Aucune annotation pour le moment",
|
||||
"No bookmark yet": "Aucun signet pour le moment",
|
||||
"Importing...": "Importation...",
|
||||
"Swipe for Brightness": "Balayer pour la luminosité",
|
||||
"Slide along the left edge": "Glissez le long du bord gauche",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Cette action supprimera tous les fichiers mis en cache. Elle est irréversible.",
|
||||
"Confirm Clear": "Confirmer le vidage",
|
||||
"Clearing...": "Vidage…",
|
||||
"Clear Cache": "Vider le cache"
|
||||
"Clear Cache": "Vider le cache",
|
||||
"No Notes": "Aucune note",
|
||||
"Capture an idea as you read": "Notez une idée pendant votre lecture",
|
||||
"No Annotations": "Aucune annotation",
|
||||
"No Bookmarks": "Aucun signet",
|
||||
"Select some text to highlight": "Sélectionnez du texte à surligner",
|
||||
"Bookmark This Page": "Ajouter cette page aux signets"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "ייבוא הדגשות והערות שיוצאו מאפליקציית קריאה אחרת.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "קובץ ייצוא של Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "אין הערות עדיין",
|
||||
"No annotation yet": "אין הערות עדיין",
|
||||
"No bookmark yet": "אין סימניות עדיין",
|
||||
"Importing...": "מייבא...",
|
||||
"Swipe for Brightness": "החלקה לכוונון בהירות",
|
||||
"Slide along the left edge": "החליקו לאורך הקצה השמאלי",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "פעולה זו תמחק את כל הקבצים השמורים במטמון. לא ניתן לבטל פעולה זו.",
|
||||
"Confirm Clear": "אישור ניקוי",
|
||||
"Clearing...": "מנקה…",
|
||||
"Clear Cache": "נקה מטמון"
|
||||
"Clear Cache": "נקה מטמון",
|
||||
"No Notes": "אין הערות",
|
||||
"Capture an idea as you read": "תעדו רעיון תוך כדי קריאה",
|
||||
"No Annotations": "אין הדגשות",
|
||||
"No Bookmarks": "אין סימניות",
|
||||
"Select some text to highlight": "בחרו טקסט להדגשה",
|
||||
"Bookmark This Page": "הוסיפו דף זה לסימניות"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "किसी अन्य रीडिंग ऐप से निर्यात किए गए हाइलाइट और नोट्स आयात करें।",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader निर्यात फ़ाइल (.mrexpt)",
|
||||
"No note yet": "अभी तक कोई नोट नहीं",
|
||||
"No annotation yet": "अभी तक कोई टिप्पणी नहीं",
|
||||
"No bookmark yet": "अभी तक कोई बुकमार्क नहीं",
|
||||
"Importing...": "आयात हो रहा है...",
|
||||
"Swipe for Brightness": "चमक के लिए स्वाइप करें",
|
||||
"Slide along the left edge": "बाएँ किनारे पर स्लाइड करें",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "इससे सभी कैश की गई फ़ाइलें हट जाएँगी. इसे पूर्ववत नहीं किया जा सकता.",
|
||||
"Confirm Clear": "साफ़ करने की पुष्टि करें",
|
||||
"Clearing...": "साफ़ किया जा रहा है…",
|
||||
"Clear Cache": "कैश साफ़ करें"
|
||||
"Clear Cache": "कैश साफ़ करें",
|
||||
"No Notes": "कोई नोट नहीं",
|
||||
"Capture an idea as you read": "पढ़ते समय कोई विचार लिख लें",
|
||||
"No Annotations": "कोई टिप्पणी नहीं",
|
||||
"No Bookmarks": "कोई बुकमार्क नहीं",
|
||||
"Select some text to highlight": "हाइलाइट करने के लिए कुछ टेक्स्ट चुनें",
|
||||
"Bookmark This Page": "इस पृष्ठ को बुकमार्क करें"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importálj egy másik olvasóalkalmazásból exportált kiemeléseket és jegyzeteket.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader exportfájl (.mrexpt)",
|
||||
"No note yet": "Még nincs jegyzet",
|
||||
"No annotation yet": "Még nincs jegyzet",
|
||||
"No bookmark yet": "Még nincs könyvjelző",
|
||||
"Importing...": "Importálás...",
|
||||
"Swipe for Brightness": "Húzás a fényerőhöz",
|
||||
"Slide along the left edge": "Húzza a bal szél mentén",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Ez törli az összes gyorsítótárazott fájlt. A művelet nem vonható vissza.",
|
||||
"Confirm Clear": "Törlés megerősítése",
|
||||
"Clearing...": "Törlés…",
|
||||
"Clear Cache": "Gyorsítótár törlése"
|
||||
"Clear Cache": "Gyorsítótár törlése",
|
||||
"No Notes": "Nincsenek jegyzetek",
|
||||
"Capture an idea as you read": "Rögzíts egy ötletet olvasás közben",
|
||||
"No Annotations": "Nincsenek megjegyzések",
|
||||
"No Bookmarks": "Nincsenek könyvjelzők",
|
||||
"Select some text to highlight": "Jelölj ki szöveget a kiemeléshez",
|
||||
"Bookmark This Page": "Oldal hozzáadása a könyvjelzőkhöz"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Impor sorotan dan catatan yang diekspor dari aplikasi baca lain.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "File ekspor Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Belum ada catatan",
|
||||
"No annotation yet": "Belum ada anotasi",
|
||||
"No bookmark yet": "Belum ada penanda",
|
||||
"Importing...": "Mengimpor...",
|
||||
"Swipe for Brightness": "Geser untuk kecerahan",
|
||||
"Slide along the left edge": "Geser di sepanjang tepi kiri",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Tindakan ini akan menghapus semua file cache. Tindakan ini tidak dapat dibatalkan.",
|
||||
"Confirm Clear": "Konfirmasi Pembersihan",
|
||||
"Clearing...": "Membersihkan…",
|
||||
"Clear Cache": "Bersihkan Cache"
|
||||
"Clear Cache": "Bersihkan Cache",
|
||||
"No Notes": "Tidak ada catatan",
|
||||
"Capture an idea as you read": "Tangkap ide saat Anda membaca",
|
||||
"No Annotations": "Tidak ada anotasi",
|
||||
"No Bookmarks": "Tidak ada penanda",
|
||||
"Select some text to highlight": "Pilih teks untuk disorot",
|
||||
"Bookmark This Page": "Tandai halaman ini"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importa evidenziazioni e note esportate da un'altra app di lettura.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "File di esportazione Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Nessuna nota ancora",
|
||||
"No annotation yet": "Nessuna annotazione ancora",
|
||||
"No bookmark yet": "Nessun segnalibro ancora",
|
||||
"Importing...": "Importazione...",
|
||||
"Swipe for Brightness": "Scorri per la luminosità",
|
||||
"Slide along the left edge": "Scorri lungo il bordo sinistro",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Questa operazione eliminerà tutti i file memorizzati nella cache. L’azione non può essere annullata.",
|
||||
"Confirm Clear": "Conferma svuotamento",
|
||||
"Clearing...": "Svuotamento…",
|
||||
"Clear Cache": "Svuota cache"
|
||||
"Clear Cache": "Svuota cache",
|
||||
"No Notes": "Nessuna nota",
|
||||
"Capture an idea as you read": "Annota un'idea mentre leggi",
|
||||
"No Annotations": "Nessuna annotazione",
|
||||
"No Bookmarks": "Nessun segnalibro",
|
||||
"Select some text to highlight": "Seleziona del testo da evidenziare",
|
||||
"Bookmark This Page": "Aggiungi questa pagina ai segnalibri"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "他の読書アプリからエクスポートしたハイライトとメモをインポートします。",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader のエクスポートファイル (.mrexpt)",
|
||||
"No note yet": "メモはまだありません",
|
||||
"No annotation yet": "注釈はまだありません",
|
||||
"No bookmark yet": "ブックマークはまだありません",
|
||||
"Importing...": "インポートしています...",
|
||||
"Swipe for Brightness": "スワイプで明るさ調整",
|
||||
"Slide along the left edge": "左端をスライド",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "すべてのキャッシュファイルを削除します。この操作は取り消せません。",
|
||||
"Confirm Clear": "削除を確認",
|
||||
"Clearing...": "削除中…",
|
||||
"Clear Cache": "キャッシュを削除"
|
||||
"Clear Cache": "キャッシュを削除",
|
||||
"No Notes": "メモがありません",
|
||||
"Capture an idea as you read": "読みながらアイデアを書き留めましょう",
|
||||
"No Annotations": "注釈がありません",
|
||||
"No Bookmarks": "ブックマークがありません",
|
||||
"Select some text to highlight": "ハイライトするテキストを選択してください",
|
||||
"Bookmark This Page": "このページをブックマーク"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "다른 독서 앱에서 내보낸 하이라이트와 메모를 가져옵니다.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader 내보내기 파일 (.mrexpt)",
|
||||
"No note yet": "아직 노트가 없습니다",
|
||||
"No annotation yet": "아직 주석이 없습니다",
|
||||
"No bookmark yet": "아직 북마크가 없습니다",
|
||||
"Importing...": "가져오는 중...",
|
||||
"Swipe for Brightness": "밝기 조절 스와이프",
|
||||
"Slide along the left edge": "왼쪽 가장자리를 따라 슬라이드",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "캐시된 모든 파일을 삭제합니다. 이 작업은 되돌릴 수 없습니다.",
|
||||
"Confirm Clear": "지우기 확인",
|
||||
"Clearing...": "지우는 중…",
|
||||
"Clear Cache": "캐시 지우기"
|
||||
"Clear Cache": "캐시 지우기",
|
||||
"No Notes": "메모 없음",
|
||||
"Capture an idea as you read": "읽으면서 떠오른 생각을 적어 보세요",
|
||||
"No Annotations": "주석 없음",
|
||||
"No Bookmarks": "북마크 없음",
|
||||
"Select some text to highlight": "하이라이트할 텍스트를 선택하세요",
|
||||
"Bookmark This Page": "이 페이지 북마크"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Import serlahan dan nota yang dieksport dari apl bacaan lain.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Fail eksport Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Belum ada nota",
|
||||
"No annotation yet": "Belum ada anotasi",
|
||||
"No bookmark yet": "Belum ada tandabuku",
|
||||
"Importing...": "Mengimport...",
|
||||
"Swipe for Brightness": "Leret untuk kecerahan",
|
||||
"Slide along the left edge": "Leret di sepanjang tepi kiri",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Tindakan ini akan memadam semua fail cache. Tindakan ini tidak boleh diundurkan.",
|
||||
"Confirm Clear": "Sahkan Pengosongan",
|
||||
"Clearing...": "Mengosongkan…",
|
||||
"Clear Cache": "Kosongkan Cache"
|
||||
"Clear Cache": "Kosongkan Cache",
|
||||
"No Notes": "Tiada nota",
|
||||
"Capture an idea as you read": "Catat idea semasa anda membaca",
|
||||
"No Annotations": "Tiada anotasi",
|
||||
"No Bookmarks": "Tiada penanda buku",
|
||||
"Select some text to highlight": "Pilih teks untuk ditonjolkan",
|
||||
"Bookmark This Page": "Tanda halaman ini"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importeer markeringen en notities die uit een andere leesapp zijn geëxporteerd.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader-exportbestand (.mrexpt)",
|
||||
"No note yet": "Nog geen notitie",
|
||||
"No annotation yet": "Nog geen annotatie",
|
||||
"No bookmark yet": "Nog geen bladwijzer",
|
||||
"Importing...": "Bezig met importeren...",
|
||||
"Swipe for Brightness": "Veeg voor helderheid",
|
||||
"Slide along the left edge": "Veeg langs de linkerrand",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Hiermee worden alle gecachte bestanden verwijderd. Dit kan niet ongedaan worden gemaakt.",
|
||||
"Confirm Clear": "Wissen bevestigen",
|
||||
"Clearing...": "Bezig met wissen…",
|
||||
"Clear Cache": "Cache wissen"
|
||||
"Clear Cache": "Cache wissen",
|
||||
"No Notes": "Geen notities",
|
||||
"Capture an idea as you read": "Leg een idee vast tijdens het lezen",
|
||||
"No Annotations": "Geen aantekeningen",
|
||||
"No Bookmarks": "Geen bladwijzers",
|
||||
"Select some text to highlight": "Selecteer tekst om te markeren",
|
||||
"Bookmark This Page": "Deze pagina als bladwijzer toevoegen"
|
||||
}
|
||||
|
||||
@@ -1738,9 +1738,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importuj zaznaczenia i notatki wyeksportowane z innej aplikacji do czytania.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Plik eksportu Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Brak notatek",
|
||||
"No annotation yet": "Brak adnotacji",
|
||||
"No bookmark yet": "Brak zakładek",
|
||||
"Importing...": "Importowanie...",
|
||||
"Swipe for Brightness": "Przesuń, aby zmienić jasność",
|
||||
"Slide along the left edge": "Przesuń wzdłuż lewej krawędzi",
|
||||
@@ -1758,5 +1755,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Spowoduje to usunięcie wszystkich plików z pamięci podręcznej. Tej operacji nie można cofnąć.",
|
||||
"Confirm Clear": "Potwierdź czyszczenie",
|
||||
"Clearing...": "Czyszczenie…",
|
||||
"Clear Cache": "Wyczyść pamięć podręczną"
|
||||
"Clear Cache": "Wyczyść pamięć podręczną",
|
||||
"No Notes": "Brak notatek",
|
||||
"Capture an idea as you read": "Zapisz pomysł podczas czytania",
|
||||
"No Annotations": "Brak adnotacji",
|
||||
"No Bookmarks": "Brak zakładek",
|
||||
"Select some text to highlight": "Wybierz tekst do podświetlenia",
|
||||
"Bookmark This Page": "Dodaj tę stronę do zakładek"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importe destaques e notas exportados de outro aplicativo de leitura.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Arquivo de exportação do Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Nenhuma nota ainda",
|
||||
"No annotation yet": "Nenhuma anotação ainda",
|
||||
"No bookmark yet": "Nenhum marcador ainda",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para o brilho",
|
||||
"Slide along the left edge": "Deslize pela borda esquerda",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Isso excluirá todos os arquivos em cache. Esta ação não pode ser desfeita.",
|
||||
"Confirm Clear": "Confirmar limpeza",
|
||||
"Clearing...": "Limpando…",
|
||||
"Clear Cache": "Limpar cache"
|
||||
"Clear Cache": "Limpar cache",
|
||||
"No Notes": "Nenhuma nota",
|
||||
"Capture an idea as you read": "Capture uma ideia enquanto lê",
|
||||
"No Annotations": "Nenhuma anotação",
|
||||
"No Bookmarks": "Nenhum marcador",
|
||||
"Select some text to highlight": "Selecione um texto para destacar",
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importe destaques e notas exportados de outro aplicativo de leitura.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Arquivo de exportação do Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Ainda não há notas",
|
||||
"No annotation yet": "Ainda não há anotações",
|
||||
"No bookmark yet": "Ainda não há marcadores",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para o brilho",
|
||||
"Slide along the left edge": "Deslize pela borda esquerda",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Isto irá eliminar todos os ficheiros em cache. Esta ação não pode ser anulada.",
|
||||
"Confirm Clear": "Confirmar limpeza",
|
||||
"Clearing...": "A limpar…",
|
||||
"Clear Cache": "Limpar cache"
|
||||
"Clear Cache": "Limpar cache",
|
||||
"No Notes": "Sem notas",
|
||||
"Capture an idea as you read": "Capture uma ideia enquanto lê",
|
||||
"No Annotations": "Sem anotações",
|
||||
"No Bookmarks": "Sem marcadores",
|
||||
"Select some text to highlight": "Selecione texto para destacar",
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores"
|
||||
}
|
||||
|
||||
@@ -1706,9 +1706,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importă evidențieri și note exportate dintr-o altă aplicație de citire.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Fișier de export Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Nicio notă încă",
|
||||
"No annotation yet": "Nicio adnotare încă",
|
||||
"No bookmark yet": "Niciun marcaj încă",
|
||||
"Importing...": "Se importă...",
|
||||
"Swipe for Brightness": "Glisează pentru luminozitate",
|
||||
"Slide along the left edge": "Glisează de-a lungul marginii stângi",
|
||||
@@ -1725,5 +1722,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Această acțiune va șterge toate fișierele din memoria cache. Acțiunea nu poate fi anulată.",
|
||||
"Confirm Clear": "Confirmă golirea",
|
||||
"Clearing...": "Se golește…",
|
||||
"Clear Cache": "Golește memoria cache"
|
||||
"Clear Cache": "Golește memoria cache",
|
||||
"No Notes": "Nicio notă",
|
||||
"Capture an idea as you read": "Notează o idee în timp ce citești",
|
||||
"No Annotations": "Nicio adnotare",
|
||||
"No Bookmarks": "Niciun marcaj",
|
||||
"Select some text to highlight": "Selectează text pentru evidențiere",
|
||||
"Bookmark This Page": "Adaugă această pagină la marcaje"
|
||||
}
|
||||
|
||||
@@ -1738,9 +1738,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Импортируйте выделения и заметки, экспортированные из другого приложения для чтения.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Файл экспорта Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Пока нет заметок",
|
||||
"No annotation yet": "Пока нет аннотаций",
|
||||
"No bookmark yet": "Пока нет закладок",
|
||||
"Importing...": "Импорт...",
|
||||
"Swipe for Brightness": "Свайп для яркости",
|
||||
"Slide along the left edge": "Проведите вдоль левого края",
|
||||
@@ -1758,5 +1755,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Это удалит все файлы из кэша. Это действие нельзя отменить.",
|
||||
"Confirm Clear": "Подтвердить очистку",
|
||||
"Clearing...": "Очистка…",
|
||||
"Clear Cache": "Очистить кэш"
|
||||
"Clear Cache": "Очистить кэш",
|
||||
"No Notes": "Нет заметок",
|
||||
"Capture an idea as you read": "Запишите мысль во время чтения",
|
||||
"No Annotations": "Нет аннотаций",
|
||||
"No Bookmarks": "Нет закладок",
|
||||
"Select some text to highlight": "Выберите текст для выделения",
|
||||
"Bookmark This Page": "Добавить эту страницу в закладки"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "වෙනත් කියවීමේ යෙදුමකින් නිර්යාත කළ ඉස්මතු කිරීම් සහ සටහන් ආයාත කරන්න.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader නිර්යාත ගොනුව (.mrexpt)",
|
||||
"No note yet": "තවම සටහනක් නැත",
|
||||
"No annotation yet": "තවම විවරණයක් නැත",
|
||||
"No bookmark yet": "තවම පොත් සලකුණක් නැත",
|
||||
"Importing...": "ආයාත වෙමින්...",
|
||||
"Swipe for Brightness": "දීප්තිය සඳහා ස්වයිප් කරන්න",
|
||||
"Slide along the left edge": "වම් කෙළවර දිගේ අදින්න",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "මෙය සියලුම හැඹිලිගත ගොනු මකා දමයි. මෙය පෙරළිය නොහැක.",
|
||||
"Confirm Clear": "හිස් කිරීම තහවුරු කරන්න",
|
||||
"Clearing...": "හිස් කරමින්…",
|
||||
"Clear Cache": "හැඹිලිය හිස් කරන්න"
|
||||
"Clear Cache": "හැඹිලිය හිස් කරන්න",
|
||||
"No Notes": "සටහන් නැත",
|
||||
"Capture an idea as you read": "කියවන විට අදහසක් සටහන් කර ගන්න",
|
||||
"No Annotations": "අනුසටහන් නැත",
|
||||
"No Bookmarks": "පොත් සලකුණු නැත",
|
||||
"Select some text to highlight": "ඉස්මතු කිරීමට පෙළක් තෝරන්න",
|
||||
"Bookmark This Page": "මෙම පිටුව පොත් සලකුණක් කරන්න"
|
||||
}
|
||||
|
||||
@@ -1738,9 +1738,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Uvozi označbe in opombe, izvožene iz druge bralne aplikacije.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Izvozna datoteka Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Še ni opombe",
|
||||
"No annotation yet": "Še ni opombe",
|
||||
"No bookmark yet": "Še ni zaznamka",
|
||||
"Importing...": "Uvažanje ...",
|
||||
"Swipe for Brightness": "Poteg za svetlost",
|
||||
"Slide along the left edge": "Povlecite ob levem robu",
|
||||
@@ -1758,5 +1755,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "To bo izbrisalo vse predpomnjene datoteke. Tega dejanja ni mogoče razveljaviti.",
|
||||
"Confirm Clear": "Potrdi čiščenje",
|
||||
"Clearing...": "Čiščenje…",
|
||||
"Clear Cache": "Počisti predpomnilnik"
|
||||
"Clear Cache": "Počisti predpomnilnik",
|
||||
"No Notes": "Ni zapiskov",
|
||||
"Capture an idea as you read": "Zabeležite zamisel med branjem",
|
||||
"No Annotations": "Ni opomb",
|
||||
"No Bookmarks": "Ni zaznamkov",
|
||||
"Select some text to highlight": "Izberite besedilo za označevanje",
|
||||
"Bookmark This Page": "Dodaj to stran med zaznamke"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Importera markeringar och anteckningar som exporterats från en annan läsapp.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader-exportfil (.mrexpt)",
|
||||
"No note yet": "Ingen anteckning än",
|
||||
"No annotation yet": "Ingen annotering än",
|
||||
"No bookmark yet": "Inget bokmärke än",
|
||||
"Importing...": "Importerar...",
|
||||
"Swipe for Brightness": "Svep för ljusstyrka",
|
||||
"Slide along the left edge": "Dra längs vänsterkanten",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Detta tar bort alla cachade filer. Åtgärden kan inte ångras.",
|
||||
"Confirm Clear": "Bekräfta rensning",
|
||||
"Clearing...": "Rensar…",
|
||||
"Clear Cache": "Rensa cache"
|
||||
"Clear Cache": "Rensa cache",
|
||||
"No Notes": "Inga anteckningar",
|
||||
"Capture an idea as you read": "Fånga en idé medan du läser",
|
||||
"No Annotations": "Inga kommentarer",
|
||||
"No Bookmarks": "Inga bokmärken",
|
||||
"Select some text to highlight": "Markera text att framhäva",
|
||||
"Bookmark This Page": "Bokmärk den här sidan"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "மற்றொரு வாசிப்பு செயலியிலிருந்து ஏற்றுமதி செய்யப்பட்ட முத்திரைகள் மற்றும் குறிப்புகளை இறக்குமதி செய்யவும்.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader ஏற்றுமதி கோப்பு (.mrexpt)",
|
||||
"No note yet": "இன்னும் குறிப்பு இல்லை",
|
||||
"No annotation yet": "இன்னும் சிறுகுறிப்பு இல்லை",
|
||||
"No bookmark yet": "இன்னும் புக்மார்க் இல்லை",
|
||||
"Importing...": "இறக்குமதி செய்கிறது...",
|
||||
"Swipe for Brightness": "ஒளிர்வுக்கு ஸ்வைப் செய்யவும்",
|
||||
"Slide along the left edge": "இடது விளிம்பில் இழுக்கவும்",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "இது தற்காலிகச் சேமிப்பில் உள்ள அனைத்துக் கோப்புகளையும் நீக்கும். இதை மீட்டெடுக்க முடியாது.",
|
||||
"Confirm Clear": "அழிப்பதை உறுதிப்படுத்து",
|
||||
"Clearing...": "அழிக்கப்படுகிறது…",
|
||||
"Clear Cache": "தற்காலிக சேமிப்பை அழி"
|
||||
"Clear Cache": "தற்காலிக சேமிப்பை அழி",
|
||||
"No Notes": "குறிப்புகள் இல்லை",
|
||||
"Capture an idea as you read": "படிக்கும்போது ஒரு கருத்தைக் குறித்து வையுங்கள்",
|
||||
"No Annotations": "சிறுகுறிப்புகள் இல்லை",
|
||||
"No Bookmarks": "புக்மார்க்குகள் இல்லை",
|
||||
"Select some text to highlight": "சிறப்பிக்க உரையைத் தேர்ந்தெடுக்கவும்",
|
||||
"Bookmark This Page": "இந்தப் பக்கத்தைப் புக்மார்க் செய்யவும்"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "นำเข้าไฮไลต์และบันทึกที่ส่งออกจากแอปอ่านหนังสืออื่น",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "ไฟล์ส่งออก Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "ยังไม่มีบันทึก",
|
||||
"No annotation yet": "ยังไม่มีคำอธิบายประกอบ",
|
||||
"No bookmark yet": "ยังไม่มีบุ๊กมาร์ก",
|
||||
"Importing...": "กำลังนำเข้า...",
|
||||
"Swipe for Brightness": "ปัดเพื่อปรับความสว่าง",
|
||||
"Slide along the left edge": "เลื่อนตามขอบด้านซ้าย",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "การดำเนินการนี้จะลบไฟล์แคชทั้งหมด ไม่สามารถยกเลิกได้",
|
||||
"Confirm Clear": "ยืนยันการล้าง",
|
||||
"Clearing...": "กำลังล้าง…",
|
||||
"Clear Cache": "ล้างแคช"
|
||||
"Clear Cache": "ล้างแคช",
|
||||
"No Notes": "ไม่มีบันทึก",
|
||||
"Capture an idea as you read": "จดบันทึกไอเดียขณะที่คุณอ่าน",
|
||||
"No Annotations": "ไม่มีคำอธิบายประกอบ",
|
||||
"No Bookmarks": "ไม่มีบุ๊กมาร์ก",
|
||||
"Select some text to highlight": "เลือกข้อความเพื่อไฮไลต์",
|
||||
"Bookmark This Page": "บุ๊กมาร์กหน้านี้"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Başka bir okuma uygulamasından dışa aktarılan vurgu ve notları içe aktarın.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader dışa aktarma dosyası (.mrexpt)",
|
||||
"No note yet": "Henüz not yok",
|
||||
"No annotation yet": "Henüz açıklama yok",
|
||||
"No bookmark yet": "Henüz yer imi yok",
|
||||
"Importing...": "İçe aktarılıyor...",
|
||||
"Swipe for Brightness": "Parlaklık için kaydır",
|
||||
"Slide along the left edge": "Sol kenar boyunca kaydır",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Bu, önbelleğe alınmış tüm dosyaları siler. Bu işlem geri alınamaz.",
|
||||
"Confirm Clear": "Temizlemeyi Onayla",
|
||||
"Clearing...": "Temizleniyor…",
|
||||
"Clear Cache": "Önbelleği Temizle"
|
||||
"Clear Cache": "Önbelleği Temizle",
|
||||
"No Notes": "Not yok",
|
||||
"Capture an idea as you read": "Okurken bir fikri not alın",
|
||||
"No Annotations": "Açıklama yok",
|
||||
"No Bookmarks": "Yer işareti yok",
|
||||
"Select some text to highlight": "Vurgulamak için metin seçin",
|
||||
"Bookmark This Page": "Bu Sayfayı Yer İşaretine Ekle"
|
||||
}
|
||||
|
||||
@@ -1738,9 +1738,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Імпортуйте виділення та нотатки, експортовані з іншого застосунку для читання.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Файл експорту Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Поки немає нотаток",
|
||||
"No annotation yet": "Поки немає анотацій",
|
||||
"No bookmark yet": "Поки немає закладок",
|
||||
"Importing...": "Імпорт...",
|
||||
"Swipe for Brightness": "Свайп для яскравості",
|
||||
"Slide along the left edge": "Проведіть уздовж лівого краю",
|
||||
@@ -1758,5 +1755,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Це видалить усі файли з кешу. Цю дію не можна скасувати.",
|
||||
"Confirm Clear": "Підтвердити очищення",
|
||||
"Clearing...": "Очищення…",
|
||||
"Clear Cache": "Очистити кеш"
|
||||
"Clear Cache": "Очистити кеш",
|
||||
"No Notes": "Немає нотаток",
|
||||
"Capture an idea as you read": "Занотуйте ідею під час читання",
|
||||
"No Annotations": "Немає анотацій",
|
||||
"No Bookmarks": "Немає закладок",
|
||||
"Select some text to highlight": "Виділіть текст для підсвічування",
|
||||
"Bookmark This Page": "Додати цю сторінку до закладок"
|
||||
}
|
||||
|
||||
@@ -1674,9 +1674,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Boshqa oʻqish ilovasidan eksport qilingan belgilashlar va eslatmalarni import qiling.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader eksport fayli (.mrexpt)",
|
||||
"No note yet": "Hali eslatma yoʻq",
|
||||
"No annotation yet": "Hali izoh yoʻq",
|
||||
"No bookmark yet": "Hali xatchoʻp yoʻq",
|
||||
"Importing...": "Import qilinmoqda...",
|
||||
"Swipe for Brightness": "Yorqinlik uchun suring",
|
||||
"Slide along the left edge": "Chap chekka bo'ylab suring",
|
||||
@@ -1692,5 +1689,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Bu barcha keshlangan fayllarni o‘chiradi. Buni qaytarib bo‘lmaydi.",
|
||||
"Confirm Clear": "Tozalashni tasdiqlash",
|
||||
"Clearing...": "Tozalanmoqda…",
|
||||
"Clear Cache": "Keshni tozalash"
|
||||
"Clear Cache": "Keshni tozalash",
|
||||
"No Notes": "Eslatmalar yoʻq",
|
||||
"Capture an idea as you read": "Oʻqish davomida fikrni yozib oling",
|
||||
"No Annotations": "Izohlar yoʻq",
|
||||
"No Bookmarks": "Xatchoʻplar yoʻq",
|
||||
"Select some text to highlight": "Belgilash uchun matn tanlang",
|
||||
"Bookmark This Page": "Bu sahifani xatchoʻpga qoʻshish"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "Nhập các đánh dấu và ghi chú được xuất từ một ứng dụng đọc khác.",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Tệp xuất Moon+ Reader (.mrexpt)",
|
||||
"No note yet": "Chưa có ghi chú",
|
||||
"No annotation yet": "Chưa có chú thích",
|
||||
"No bookmark yet": "Chưa có dấu trang",
|
||||
"Importing...": "Đang nhập...",
|
||||
"Swipe for Brightness": "Vuốt để chỉnh độ sáng",
|
||||
"Slide along the left edge": "Vuốt dọc theo cạnh trái",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "Thao tác này sẽ xóa tất cả tệp trong bộ nhớ đệm. Không thể hoàn tác.",
|
||||
"Confirm Clear": "Xác nhận xóa",
|
||||
"Clearing...": "Đang xóa…",
|
||||
"Clear Cache": "Xóa bộ nhớ đệm"
|
||||
"Clear Cache": "Xóa bộ nhớ đệm",
|
||||
"No Notes": "Không có ghi chú",
|
||||
"Capture an idea as you read": "Ghi lại một ý tưởng khi bạn đọc",
|
||||
"No Annotations": "Không có chú thích",
|
||||
"No Bookmarks": "Không có dấu trang",
|
||||
"Select some text to highlight": "Chọn một đoạn văn bản để tô sáng",
|
||||
"Bookmark This Page": "Đánh dấu trang này"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "导入从其他阅读应用导出的划线和笔记。",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader 导出文件 (.mrexpt)",
|
||||
"No note yet": "尚无笔记",
|
||||
"No annotation yet": "尚无标注",
|
||||
"No bookmark yet": "尚无书签",
|
||||
"Importing...": "正在导入...",
|
||||
"Swipe for Brightness": "滑动调节亮度",
|
||||
"Slide along the left edge": "沿左侧边缘滑动",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "这将删除所有缓存文件,此操作无法撤销。",
|
||||
"Confirm Clear": "确认清除",
|
||||
"Clearing...": "正在清除…",
|
||||
"Clear Cache": "清除缓存"
|
||||
"Clear Cache": "清除缓存",
|
||||
"No Notes": "暂无笔记",
|
||||
"Capture an idea as you read": "阅读时记下灵感",
|
||||
"No Annotations": "暂无注释",
|
||||
"No Bookmarks": "暂无书签",
|
||||
"Select some text to highlight": "选择文本以划线",
|
||||
"Bookmark This Page": "为此页添加书签"
|
||||
}
|
||||
|
||||
@@ -1642,9 +1642,6 @@
|
||||
"Import highlights and notes exported from another reading app.": "匯入從其他閱讀應用程式匯出的劃線與筆記。",
|
||||
"Moon+ Reader": "Moon+ Reader",
|
||||
"Moon+ Reader export file (.mrexpt)": "Moon+ Reader 匯出檔案 (.mrexpt)",
|
||||
"No note yet": "尚無筆記",
|
||||
"No annotation yet": "尚無註解",
|
||||
"No bookmark yet": "尚無書籤",
|
||||
"Importing...": "正在匯入...",
|
||||
"Swipe for Brightness": "滑動調整亮度",
|
||||
"Slide along the left edge": "沿左側邊緣滑動",
|
||||
@@ -1659,5 +1656,11 @@
|
||||
"This will delete all cached files. This cannot be undone.": "這將刪除所有快取檔案,此操作無法復原。",
|
||||
"Confirm Clear": "確認清除",
|
||||
"Clearing...": "正在清除…",
|
||||
"Clear Cache": "清除快取"
|
||||
"Clear Cache": "清除快取",
|
||||
"No Notes": "尚無筆記",
|
||||
"Capture an idea as you read": "閱讀時記下靈感",
|
||||
"No Annotations": "尚無註解",
|
||||
"No Bookmarks": "尚無書籤",
|
||||
"Select some text to highlight": "選取文字以劃線",
|
||||
"Bookmark This Page": "將此頁加入書籤"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { getFooterBarPosition } from '@/app/reader/components/footerbar/position';
|
||||
|
||||
describe('getFooterBarPosition', () => {
|
||||
it('pins the mobile footer layout to the viewport when no sidebar is pinned', () => {
|
||||
expect(getFooterBarPosition(true, false)).toBe('fixed');
|
||||
});
|
||||
|
||||
it('anchors the footer inside the grid cell when the sidebar is pinned', () => {
|
||||
// Regression: a viewport-fixed footer slides under a pinned sidebar. When
|
||||
// the sidebar is pinned it occupies horizontal space, so the footer must
|
||||
// anchor within the book's grid cell and start at the sidebar's right edge.
|
||||
expect(getFooterBarPosition(true, true)).toBe('absolute');
|
||||
});
|
||||
|
||||
it('keeps the desktop footer anchored in the grid cell regardless of pinning', () => {
|
||||
expect(getFooterBarPosition(false, false)).toBe('absolute');
|
||||
expect(getFooterBarPosition(false, true)).toBe('absolute');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { getPanelTopInset } from '@/utils/insets';
|
||||
|
||||
const insets = (top: number) => ({ top, right: 0, bottom: 0, left: 0 });
|
||||
|
||||
describe('getPanelTopInset', () => {
|
||||
it('respects the status bar on non-mobile panels when system UI is visible', () => {
|
||||
// Regression for #4089: a tablet/desktop sidebar (isMobile === false) used to
|
||||
// collapse its top padding to 0, letting the status bar obscure the toolbar.
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: false,
|
||||
isFullHeightInMobile: false,
|
||||
systemUIVisible: true,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: insets(0),
|
||||
}),
|
||||
).toBe(24);
|
||||
});
|
||||
|
||||
it('uses the larger of the safe-area inset and the status bar height', () => {
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: false,
|
||||
isFullHeightInMobile: false,
|
||||
systemUIVisible: true,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: insets(40),
|
||||
}),
|
||||
).toBe(40);
|
||||
});
|
||||
|
||||
it('uses the safe-area inset alone on non-mobile panels when system UI is hidden', () => {
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: false,
|
||||
isFullHeightInMobile: false,
|
||||
systemUIVisible: false,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: insets(0),
|
||||
}),
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
it('pads a full-height mobile sheet with the status bar', () => {
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: true,
|
||||
isFullHeightInMobile: true,
|
||||
systemUIVisible: true,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: insets(0),
|
||||
}),
|
||||
).toBe(24);
|
||||
});
|
||||
|
||||
it('does not pad a partial-height mobile sheet that is not at the top', () => {
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: true,
|
||||
isFullHeightInMobile: false,
|
||||
systemUIVisible: true,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: insets(0),
|
||||
}),
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
it('treats missing safe-area insets as zero', () => {
|
||||
expect(
|
||||
getPanelTopInset({
|
||||
isMobile: false,
|
||||
isFullHeightInMobile: false,
|
||||
systemUIVisible: false,
|
||||
statusBarHeight: 24,
|
||||
safeAreaInsets: null,
|
||||
}),
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { MdOutlineBookmarkAdd, MdOutlineBookmark } from 'react-icons/md';
|
||||
import { RiBookmarkLine, RiBookmarkFill } from 'react-icons/ri';
|
||||
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
@@ -12,6 +12,7 @@ import Button from '@/components/Button';
|
||||
import { getCurrentPage } from '@/utils/book';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { isCfiInLocation } from '@/utils/cfi';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
|
||||
interface BookmarkTogglerProps {
|
||||
bookKey: string;
|
||||
@@ -26,6 +27,7 @@ const BookmarkToggler: React.FC<BookmarkTogglerProps> = ({ bookKey }) => {
|
||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||
const config = getConfig(bookKey);
|
||||
const progress = getProgress(bookKey);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
|
||||
const toggleBookmark = () => {
|
||||
const bookData = getBookData(bookKey);
|
||||
@@ -110,9 +112,9 @@ const BookmarkToggler: React.FC<BookmarkTogglerProps> = ({ bookKey }) => {
|
||||
<Button
|
||||
icon={
|
||||
isBookmarked ? (
|
||||
<MdOutlineBookmark className='text-base-content' />
|
||||
<RiBookmarkFill className='text-base-content' size={iconSize18} />
|
||||
) : (
|
||||
<MdOutlineBookmarkAdd className='text-base-content' />
|
||||
<RiBookmarkLine className='text-base-content' size={iconSize18} />
|
||||
)
|
||||
}
|
||||
onClick={toggleBookmark}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { IconType } from 'react-icons';
|
||||
|
||||
interface EmptyStateProps {
|
||||
Icon: IconType;
|
||||
label: string;
|
||||
hint?: string;
|
||||
action?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact empty-state for reader side panels (annotations, bookmarks, notes):
|
||||
* a large muted icon above a title and either a one-line hint or an action
|
||||
* (e.g. a button), matching the library empty-state's tone.
|
||||
*/
|
||||
const EmptyState: React.FC<EmptyStateProps> = ({ Icon, label, hint, action, className }) => (
|
||||
<div
|
||||
className={clsx(
|
||||
'flex select-none flex-col items-center justify-center gap-2 px-6 text-center',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Icon className='text-base-content/55 mb-3' aria-hidden='true' size='8rem' />
|
||||
<p className='text-base-content text-sm font-semibold'>{label}</p>
|
||||
{hint && <p className='text-base-content/45 text-sm'>{hint}</p>}
|
||||
{action && <div className='mt-2'>{action}</div>}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default EmptyState;
|
||||
@@ -1,7 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { PiDotsThreeVerticalBold } from 'react-icons/pi';
|
||||
import { VscLibrary } from 'react-icons/vsc';
|
||||
import { MdOutlineMenu } from 'react-icons/md';
|
||||
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
@@ -76,7 +76,6 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
const [isMetaHashDialogOpen, setIsMetaHashDialogOpen] = useState(false);
|
||||
const [headerWidth, setHeaderWidth] = useState(0);
|
||||
const view = getView(bookKey);
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
|
||||
const docs = view?.renderer.getContents() ?? [];
|
||||
@@ -240,7 +239,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
toggleButton={
|
||||
annotationQuickAction === 'highlight' || annotationQuickAction === null ? (
|
||||
<HighlighterIcon
|
||||
size={iconSize16}
|
||||
size={iconSize18}
|
||||
tipColor={annotationQuickAction === null ? '#8F8F8F' : highlightHexColor}
|
||||
tipStyle={{
|
||||
opacity: annotationQuickAction === null ? 0.5 : 0.8,
|
||||
@@ -248,7 +247,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<AnnotationToolQuickActionIcon size={iconSize16} />
|
||||
<AnnotationToolQuickActionIcon size={iconSize18} />
|
||||
)
|
||||
}
|
||||
onToggle={handleToggleDropdown}
|
||||
@@ -287,8 +286,8 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
<Dropdown
|
||||
label={_('View Options')}
|
||||
className='exclude-title-bar-mousedown dropdown-bottom dropdown-end'
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0'
|
||||
toggleButton={<PiDotsThreeVerticalBold size={iconSize16} />}
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0 mt-0'
|
||||
toggleButton={<MdOutlineMenu />}
|
||||
onToggle={handleToggleDropdown}
|
||||
>
|
||||
<ViewMenu
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { LuNotebookPen } from 'react-icons/lu';
|
||||
import { RiQuillPenLine } from 'react-icons/ri';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -19,7 +19,7 @@ const NotebookToggler: React.FC<NotebookTogglerProps> = ({ bookKey }) => {
|
||||
const { setHoveredBookKey } = useReaderStore();
|
||||
const { sideBarBookKey, setSideBarBookKey } = useSidebarStore();
|
||||
const { isNotebookVisible, toggleNotebook } = useNotebookStore();
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
|
||||
const handleToggleSidebar = () => {
|
||||
if (appService?.isMobile) {
|
||||
@@ -36,9 +36,9 @@ const NotebookToggler: React.FC<NotebookTogglerProps> = ({ bookKey }) => {
|
||||
<Button
|
||||
icon={
|
||||
sideBarBookKey == bookKey && isNotebookVisible ? (
|
||||
<LuNotebookPen size={iconSize16} className='text-base-content' />
|
||||
<RiQuillPenLine size={iconSize18} className='text-base-content' />
|
||||
) : (
|
||||
<LuNotebookPen size={iconSize16} className='text-base-content' />
|
||||
<RiQuillPenLine size={iconSize18} className='text-base-content' />
|
||||
)
|
||||
}
|
||||
onClick={handleToggleSidebar}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { RiTranslateAi } from 'react-icons/ri';
|
||||
import { RiTranslate } from 'react-icons/ri';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { saveViewSettings } from '@/helpers/settings';
|
||||
import { isTranslationAvailable } from '@/services/translators/utils';
|
||||
import Button from '@/components/Button';
|
||||
@@ -15,6 +16,7 @@ const TranslationToggler = ({ bookKey }: { bookKey: string }) => {
|
||||
const { getBookData } = useBookDataStore();
|
||||
const { getViewSettings, setViewSettings, setHoveredBookKey } = useReaderStore();
|
||||
|
||||
const iconSize20 = useResponsiveSize(20);
|
||||
const bookData = getBookData(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const [translationEnabled, setTranslationEnabled] = useState(viewSettings.translationEnabled!);
|
||||
@@ -43,7 +45,10 @@ const TranslationToggler = ({ bookKey }: { bookKey: string }) => {
|
||||
return (
|
||||
<Button
|
||||
icon={
|
||||
<RiTranslateAi className={translationEnabled ? 'text-blue-500' : 'text-base-content'} />
|
||||
<RiTranslate
|
||||
className={translationEnabled ? 'text-blue-500' : 'text-base-content'}
|
||||
size={iconSize20}
|
||||
/>
|
||||
}
|
||||
aria-label={_('Toggle Translation')}
|
||||
disabled={!translationAvailable && !translationEnabled}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { debounce } from '@/utils/debounce';
|
||||
import { RSVPControl } from '../rsvp';
|
||||
import MobileFooterBar from './MobileFooterBar';
|
||||
import DesktopFooterBar from './DesktopFooterBar';
|
||||
import { getFooterBarPosition } from './position';
|
||||
import TTSControl from '../tts/TTSControl';
|
||||
|
||||
const FooterBar: React.FC<FooterBarProps> = ({
|
||||
@@ -29,7 +30,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
const { getConfig, setConfig, getBookData } = useBookDataStore();
|
||||
const { hoveredBookKey, setHoveredBookKey } = useReaderStore();
|
||||
const { getView, getViewState, getProgress, getViewSettings } = useReaderStore();
|
||||
const { isSideBarVisible, setSideBarVisible } = useSidebarStore();
|
||||
const { isSideBarVisible, isSideBarPinned, setSideBarVisible } = useSidebarStore();
|
||||
const { acquireBackKeyInterception, releaseBackKeyInterception } = useDeviceControlStore();
|
||||
|
||||
const view = getView(bookKey);
|
||||
@@ -222,7 +223,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
!forceMobileLayout && 'sm:h-[52px] sm:bg-base-100 sm:border-none',
|
||||
'not-eink:border-base-300/50 eink:border-base-content border-t',
|
||||
'transition-[opacity,transform] duration-300',
|
||||
forceMobileLayout || window.innerWidth < 640 ? 'fixed' : 'absolute',
|
||||
getFooterBarPosition(forceMobileLayout || window.innerWidth < 640, isSideBarPinned),
|
||||
appService?.hasRoundedWindow && 'rounded-window-bottom-right',
|
||||
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-bottom-left',
|
||||
isHoveredAnim && 'hover-bar-anim',
|
||||
|
||||
@@ -8,6 +8,7 @@ import { MdOutlineHeadphones as TTSIcon } from 'react-icons/md';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import Button from '@/components/Button';
|
||||
import { Insets } from '@/types/misc';
|
||||
@@ -31,6 +32,8 @@ export const NavigationBar: React.FC<NavigationBarProps> = ({
|
||||
const _ = useTranslation();
|
||||
const { appService } = useEnv();
|
||||
const { getViewState } = useReaderStore();
|
||||
const { isSideBarVisible, isSideBarPinned } = useSidebarStore();
|
||||
|
||||
const viewState = getViewState(bookKey);
|
||||
const tocIconSize = useResponsiveSize(23);
|
||||
const fontIconSize = useResponsiveSize(18);
|
||||
@@ -49,11 +52,13 @@ export const NavigationBar: React.FC<NavigationBarProps> = ({
|
||||
: navPadding,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
label={_('Table of Contents')}
|
||||
icon={<TOCIcon size={tocIconSize} />}
|
||||
onClick={() => onSetActionTab('toc')}
|
||||
/>
|
||||
{isSideBarVisible && isSideBarPinned ? null : (
|
||||
<Button
|
||||
label={_('Table of Contents')}
|
||||
icon={<TOCIcon size={tocIconSize} />}
|
||||
onClick={() => onSetActionTab('toc')}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
label={_('Color')}
|
||||
icon={<ColorIcon className={clsx(actionTab === 'color' && 'text-blue-500')} />}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
export type FooterBarPosition = 'fixed' | 'absolute';
|
||||
|
||||
/**
|
||||
* Where the footer bar anchors. The mobile footer layout pins to the viewport
|
||||
* (`fixed`) so its slide-up panels sit at the bottom of the screen. But a pinned
|
||||
* sidebar occupies horizontal space, and a viewport-fixed footer would slide
|
||||
* under it — so when the sidebar is pinned the footer anchors inside the book's
|
||||
* grid cell (`absolute`) instead, starting at the sidebar's right edge. The
|
||||
* desktop layout is always grid-cell anchored.
|
||||
*/
|
||||
export const getFooterBarPosition = (
|
||||
useMobileFooterLayout: boolean,
|
||||
isSideBarPinned: boolean,
|
||||
): FooterBarPosition => (useMobileFooterLayout && !isSideBarPinned ? 'fixed' : 'absolute');
|
||||
@@ -2,7 +2,7 @@ import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import { FiSearch } from 'react-icons/fi';
|
||||
import { LuNotebookPen } from 'react-icons/lu';
|
||||
import { RiQuillPenLine } from 'react-icons/ri';
|
||||
import { MdArrowBackIosNew, MdOutlinePushPin, MdPushPin } from 'react-icons/md';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
@@ -23,12 +23,12 @@ const NotebookHeader: React.FC<{
|
||||
showSearchButton = true,
|
||||
}) => {
|
||||
const _ = useTranslation();
|
||||
const iconSize14 = useResponsiveSize(14);
|
||||
const iconSize15 = useResponsiveSize(15);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
return (
|
||||
<div className='notebook-header relative flex h-11 items-center px-3' dir='ltr'>
|
||||
<div className='absolute inset-0 z-[-1] flex items-center justify-center space-x-2'>
|
||||
<LuNotebookPen size={iconSize18} />
|
||||
<RiQuillPenLine size={iconSize18} />
|
||||
<div className='notebook-title hidden text-sm font-medium sm:flex'>{_('Notebook')}</div>
|
||||
</div>
|
||||
<div className='flex w-full items-center gap-x-4'>
|
||||
@@ -40,7 +40,7 @@ const NotebookHeader: React.FC<{
|
||||
isPinned ? 'bg-base-300' : 'bg-base-300/65',
|
||||
)}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={iconSize14} /> : <MdOutlinePushPin size={iconSize14} />}
|
||||
{isPinned ? <MdPushPin size={iconSize15} /> : <MdOutlinePushPin size={iconSize15} />}
|
||||
</button>
|
||||
<button
|
||||
title={_('Close')}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { RiQuillPenLine } from 'react-icons/ri';
|
||||
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
@@ -17,6 +18,7 @@ import { BookNote } from '@/types/book';
|
||||
import { uniqueId } from '@/utils/misc';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { getBookDirFromLanguage } from '@/utils/book';
|
||||
import { getPanelTopInset } from '@/utils/insets';
|
||||
import { Overlay } from '@/components/Overlay';
|
||||
import { saveSysSettings } from '@/helpers/settings';
|
||||
import { NOTE_PREFIX } from '@/types/view';
|
||||
@@ -27,6 +29,7 @@ import NotebookHeader from './Header';
|
||||
import NoteEditor from './NoteEditor';
|
||||
import SearchBar from './SearchBar';
|
||||
import NotebookTabNavigation from './NotebookTabNavigation';
|
||||
import EmptyState from '../EmptyState';
|
||||
|
||||
const MIN_NOTEBOOK_WIDTH = 0.15;
|
||||
const MAX_NOTEBOOK_WIDTH = 0.45;
|
||||
@@ -247,6 +250,8 @@ const Notebook: React.FC = ({}) => {
|
||||
|
||||
const hasSearchResults = filteredAnnotationNotes.length > 0 || filteredExcerptNotes.length > 0;
|
||||
const hasAnyNotes = annotationNotes.length > 0 || excerptNotes.length > 0;
|
||||
const isNotesTabEmpty =
|
||||
!notebookNewAnnotation && !notebookEditAnnotation && !isSearchBarVisible && !hasAnyNotes;
|
||||
|
||||
return isNotebookVisible ? (
|
||||
<>
|
||||
@@ -273,11 +278,13 @@ const Notebook: React.FC = ({}) => {
|
||||
width: isMobile ? '100%' : `${notebookWidth}`,
|
||||
maxWidth: isMobile ? '100%' : `${MAX_NOTEBOOK_WIDTH * 100}%`,
|
||||
position: isMobile ? 'fixed' : isNotebookPinned ? 'relative' : 'absolute',
|
||||
paddingTop: isFullHeightInMobile
|
||||
? systemUIVisible
|
||||
? `${Math.max(safeAreaInsets?.top || 0, statusBarHeight)}px`
|
||||
: `${safeAreaInsets?.top || 0}px`
|
||||
: '0px',
|
||||
paddingTop: `${getPanelTopInset({
|
||||
isMobile,
|
||||
isFullHeightInMobile,
|
||||
systemUIVisible,
|
||||
statusBarHeight,
|
||||
safeAreaInsets,
|
||||
})}px`,
|
||||
}}
|
||||
>
|
||||
<style jsx>{`
|
||||
@@ -347,6 +354,14 @@ const Notebook: React.FC = ({}) => {
|
||||
<div className='flex min-h-0 flex-1 flex-col'>
|
||||
<AIAssistant key={activeConversationId ?? 'new'} bookKey={sideBarBookKey} />
|
||||
</div>
|
||||
) : isNotesTabEmpty ? (
|
||||
<div className='flex flex-grow items-center justify-center overflow-y-auto px-3'>
|
||||
<EmptyState
|
||||
Icon={RiQuillPenLine}
|
||||
label={_('No Notes')}
|
||||
hint={_('Capture an idea as you read')}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex-grow overflow-y-auto px-3'>
|
||||
{isSearchBarVisible && searchResults && !hasSearchResults && hasAnyNotes && (
|
||||
@@ -430,14 +445,6 @@ const Notebook: React.FC = ({}) => {
|
||||
<BooknoteItem key={`${index}-${item.cfi}`} bookKey={sideBarBookKey} item={item} />
|
||||
))}
|
||||
</ul>
|
||||
{!notebookNewAnnotation &&
|
||||
!notebookEditAnnotation &&
|
||||
!isSearchBarVisible &&
|
||||
!hasAnyNotes && (
|
||||
<div className='flex h-32 items-center justify-center text-gray-500'>
|
||||
<p className='font-size-sm text-center'>{_('No note yet')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
|
||||
@@ -256,9 +256,17 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, isNearest, o
|
||||
<span className='truncate text-sm text-gray-500 sm:text-xs'>{createdAtLabel}</span>
|
||||
</div>
|
||||
<div
|
||||
className={clsx('flex items-center justify-end gap-3', isEditable && 'w-full')}
|
||||
className={clsx('flex items-center justify-end gap-4', isEditable && 'w-full')}
|
||||
dir='ltr'
|
||||
>
|
||||
<button
|
||||
onClick={deleteNote.bind(null, item)}
|
||||
className='btn btn-ghost btn-xs p-0 text-red-500 opacity-0 transition duration-300 ease-in-out hover:bg-transparent group-focus-within:opacity-100 group-hover:opacity-100'
|
||||
aria-label={_('Delete')}
|
||||
>
|
||||
<MdDelete size={size18} />
|
||||
</button>
|
||||
|
||||
{isEditable && (
|
||||
<button
|
||||
onClick={item.type === 'bookmark' ? editBookmark : editNote.bind(null, item)}
|
||||
@@ -268,14 +276,6 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, isNearest, o
|
||||
<MdEdit size={size18} />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={deleteNote.bind(null, item)}
|
||||
className='btn btn-ghost btn-xs p-0 text-red-500 opacity-0 transition duration-300 ease-in-out hover:bg-transparent group-focus-within:opacity-100 group-hover:opacity-100'
|
||||
aria-label={_('Delete')}
|
||||
>
|
||||
<MdDelete size={size18} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
|
||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import * as CFI from 'foliate-js/epubcfi.js';
|
||||
import { PiNotePencil } from 'react-icons/pi';
|
||||
import { RiBookmark3Line, RiBookmarkLine } from 'react-icons/ri';
|
||||
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -12,7 +14,9 @@ import { findNearestCfi } from '@/utils/cfi';
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { BookNote, BooknoteGroup, BookNoteType } from '@/types/book';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import BooknoteItem from './BooknoteItem';
|
||||
import EmptyState from '../EmptyState';
|
||||
|
||||
type FlatBooknoteRow =
|
||||
| { kind: 'group-header'; key: string; group: BooknoteGroup }
|
||||
@@ -216,12 +220,26 @@ const BooknoteView: React.FC<{
|
||||
<div ref={containerRef} className='booknote-list rounded pt-2' role='tree'>
|
||||
{isEmpty ? (
|
||||
<div
|
||||
className='flex items-center justify-center text-gray-500'
|
||||
className='flex items-center justify-center overflow-hidden'
|
||||
style={{ height: containerHeight }}
|
||||
>
|
||||
<p className='font-size-sm text-center'>
|
||||
{type === 'annotation' ? _('No annotation yet') : _('No bookmark yet')}
|
||||
</p>
|
||||
<EmptyState
|
||||
Icon={type === 'annotation' ? PiNotePencil : RiBookmark3Line}
|
||||
label={type === 'annotation' ? _('No Annotations') : _('No Bookmarks')}
|
||||
hint={type === 'annotation' ? _('Select some text to highlight') : undefined}
|
||||
action={
|
||||
type === 'bookmark' ? (
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-contrast h-9 min-h-0 max-w-full flex-nowrap gap-1.5 rounded-lg px-4 text-sm font-medium'
|
||||
onClick={() => eventDispatcher.dispatch('toggle-bookmark', { bookKey })}
|
||||
>
|
||||
<RiBookmarkLine className='shrink-0 text-base' />
|
||||
<span className='min-w-0 truncate'>{_('Bookmark This Page')}</span>
|
||||
</button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
|
||||
@@ -109,9 +109,11 @@ const SidebarContent: React.FC<{
|
||||
</div>
|
||||
<div
|
||||
className='flex-shrink-0'
|
||||
style={{
|
||||
paddingBottom: 'calc(env(safe-area-inset-bottom, 0px) / 2)',
|
||||
}}
|
||||
style={
|
||||
{
|
||||
// paddingBottom: 'calc(env(safe-area-inset-bottom, 0px) / 2)',
|
||||
}
|
||||
}
|
||||
>
|
||||
<TabNavigation activeTab={activeTab} onTabChange={handleTabChange} />
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ const SidebarHeader: React.FC<{
|
||||
const _ = useTranslation();
|
||||
const headerRef = useRef<HTMLDivElement>(null);
|
||||
const { isTrafficLightVisible } = useTrafficLight(headerRef);
|
||||
const iconSize14 = useResponsiveSize(14);
|
||||
const iconSize15 = useResponsiveSize(15);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
const iconSize22 = useResponsiveSize(22);
|
||||
|
||||
@@ -80,7 +80,7 @@ const SidebarHeader: React.FC<{
|
||||
isPinned ? 'bg-base-300' : 'bg-base-300/65',
|
||||
)}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={iconSize14} /> : <MdOutlinePushPin size={iconSize14} />}
|
||||
{isPinned ? <MdPushPin size={iconSize15} /> : <MdOutlinePushPin size={iconSize15} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { getBookDirFromLanguage } from '@/utils/book';
|
||||
import { getPanelTopInset } from '@/utils/insets';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useSwipeToDismiss } from '@/hooks/useSwipeToDismiss';
|
||||
import { usePanelResize } from '@/hooks/usePanelResize';
|
||||
@@ -197,11 +198,13 @@ const SideBar = ({}) => {
|
||||
width: isMobile ? '100%' : `${sideBarWidth}`,
|
||||
maxWidth: isMobile ? '100%' : `${MAX_SIDEBAR_WIDTH * 100}%`,
|
||||
position: isMobile ? 'fixed' : isSideBarPinned ? 'relative' : 'absolute',
|
||||
paddingTop: isFullHeightInMobile
|
||||
? systemUIVisible
|
||||
? `${Math.max(safeAreaInsets?.top || 0, statusBarHeight)}px`
|
||||
: `${safeAreaInsets?.top || 0}px`
|
||||
: '0px',
|
||||
paddingTop: `${getPanelTopInset({
|
||||
isMobile,
|
||||
isFullHeightInMobile,
|
||||
systemUIVisible,
|
||||
statusBarHeight,
|
||||
safeAreaInsets,
|
||||
})}px`,
|
||||
}}
|
||||
>
|
||||
<style jsx>{`
|
||||
|
||||
@@ -18,6 +18,9 @@ const TabNavigation: React.FC<{
|
||||
const { settings } = useSettingsStore();
|
||||
const aiEnabled = settings?.aiSettings?.enabled ?? false;
|
||||
|
||||
const forceMobileLayout =
|
||||
!!appService?.isMobile && window.innerWidth >= 640 && window.innerWidth <= window.innerHeight;
|
||||
const isMobile = forceMobileLayout || window.innerWidth < 640 || window.innerHeight < 640;
|
||||
const tabs = ['toc', 'annotations', 'bookmarks', ...(aiEnabled ? ['history'] : [])];
|
||||
|
||||
const getTabLabel = (tab: string) => {
|
||||
@@ -40,6 +43,7 @@ const TabNavigation: React.FC<{
|
||||
className={clsx(
|
||||
'bottom-tab border-base-300/50 bg-base-200 flex w-full border-t',
|
||||
appService?.hasRoundedWindow && 'rounded-window-bottom-left',
|
||||
isMobile && 'h-[65px]',
|
||||
)}
|
||||
dir='ltr'
|
||||
>
|
||||
@@ -49,8 +53,9 @@ const TabNavigation: React.FC<{
|
||||
tabIndex={0}
|
||||
role='button'
|
||||
className={clsx(
|
||||
'm-1.5 flex-1 cursor-pointer rounded-lg p-2 transition-colors duration-200',
|
||||
'flex-1 m-1.5 cursor-pointer rounded-lg transition-colors duration-200',
|
||||
activeTab === tab && 'bg-base-300/85',
|
||||
isMobile ? 'p-3' : 'p-2',
|
||||
)}
|
||||
onClick={() => onTabChange(tab)}
|
||||
onKeyDown={(e) => {
|
||||
@@ -62,7 +67,7 @@ const TabNavigation: React.FC<{
|
||||
title={getTabLabel(tab)}
|
||||
aria-label={getTabLabel(tab)}
|
||||
>
|
||||
<div className='m-0 flex h-6 items-center p-0'>
|
||||
<div className={clsx('flex h-6 items-center p-0', isMobile ? 'm-0.5' : 'm-0')}>
|
||||
{tab === 'toc' ? (
|
||||
<IoIosList className='mx-auto' />
|
||||
) : tab === 'annotations' ? (
|
||||
|
||||
@@ -9,7 +9,10 @@ export function HighlighterIcon({
|
||||
return GenIcon({
|
||||
tag: 'svg',
|
||||
attr: {
|
||||
viewBox: '0 0 256 256',
|
||||
// Tight vertical crop: the artwork spans y 8–224, so the default
|
||||
// `0 0 256 256` left a 32px gap below and only 8px above. Cropping to
|
||||
// the artwork's vertical bounds removes the asymmetric bottom padding.
|
||||
viewBox: '0 8 256 202',
|
||||
fill: 'none',
|
||||
},
|
||||
child: [
|
||||
|
||||
@@ -21,3 +21,30 @@ export const getViewInsets = (viewSettings: ViewSettings) => {
|
||||
left: showFooter && isVertical ? fullMarginLeftPx : compactMarginLeftPx,
|
||||
} as Insets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Top padding (px) for a slide-in panel (sidebar / notebook) so its toolbar
|
||||
* clears the device status bar, mirroring the reader header.
|
||||
*
|
||||
* A partial-height mobile bottom sheet doesn't reach the top of the screen, so
|
||||
* it needs no padding. Every other case (full-height mobile sheet, or a
|
||||
* tablet/desktop panel anchored to the top) clears the safe-area inset, growing
|
||||
* to the status bar height when the system UI is visible.
|
||||
*/
|
||||
export const getPanelTopInset = ({
|
||||
isMobile,
|
||||
isFullHeightInMobile,
|
||||
systemUIVisible,
|
||||
statusBarHeight,
|
||||
safeAreaInsets,
|
||||
}: {
|
||||
isMobile: boolean;
|
||||
isFullHeightInMobile: boolean;
|
||||
systemUIVisible: boolean;
|
||||
statusBarHeight: number;
|
||||
safeAreaInsets: Insets | null;
|
||||
}): number => {
|
||||
if (isMobile && !isFullHeightInMobile) return 0;
|
||||
const top = safeAreaInsets?.top || 0;
|
||||
return systemUIVisible ? Math.max(top, statusBarHeight) : top;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user