diff --git a/USER_GUIDE.md b/USER_GUIDE.md
index e97fc4fd..51e284c5 100644
--- a/USER_GUIDE.md
+++ b/USER_GUIDE.md
@@ -131,12 +131,12 @@ If you don't already have the plugin installed:
3. Open Calibre → Preferences → Plugins → Load plugin from file → Select the zip file.
4. Restart Calibre.
-#### Configuring the Crosspoint Plugin in Calibre
+#### Configuring the CrossPoint Plugin in Calibre
1. In Calibre select Preferences.
2. In the Preferences dialog select Plugins.
3. In Plugins search for "crosspoint".
4. Click on "Customize plugin".
-5. Update the value for "Host" to match the IP for the x4.
+5. Update the value for "Host" to match the IP for your device.
6. Leave the other settings as they are.
7. [optional] Modify the "Upload path" to point to a subfolder other than the root "/" folder. Enter this as a path relative to the root folder. Example: `/mybooks`
8. Restart Calibre.
@@ -145,20 +145,20 @@ If you don't already have the plugin installed:
#### Uploading Books
-To upload a book using the Crosspoint plugin in Calibre:
+To upload a book using the CrossPoint plugin in Calibre:
1. On the device: File Transfer -> Calibre Wireless, then join a network.
2. Select one or more books.
-3. Right click on that selection.
+3. Right-click on that selection.
4. Select "Send to Device" > "Send to main memory"
-The Crosspoint plugin will connect to the x4, create a folder for the book's author in the root folder (or the folder you configured for the plugin), then copy the book into that folder.
+The CrossPoint plugin will connect to your device, create a folder for the book's author in the root folder (or the folder you configured for the plugin), then copy the book into that folder.
#### Removing a Book
-Books cannot be removed from the x4 through Calibre. Use the web interface instead.
+Books cannot be removed from your device through Calibre. Use the web interface instead.
### 3.6 Settings
diff --git a/lib/hal/HalDisplay.h b/lib/hal/HalDisplay.h
index 9b35254a..7b21a72f 100644
--- a/lib/hal/HalDisplay.h
+++ b/lib/hal/HalDisplay.h
@@ -55,9 +55,9 @@ class HalDisplay {
void preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
// Display the framebuffer as the base frame for a grayscale overlay that
- // follows. X3 uses the OEM differential base waveform ("AA-pre-BW(mid)");
- // other panels display normally with `fallback` mode (previous behavior).
- // Deliberately does NOT force the X3 resync that displayBuffer(HALF) does.
+ // follows. On X3, HALF fallback first requests a resync to match
+ // displayBuffer(HALF); FAST fallback keeps the OEM differential base waveform
+ // ("AA-pre-BW(mid)"). Other panels display normally with `fallback` mode.
void displayGrayscaleBase(RefreshMode fallback = HALF_REFRESH, bool turnOffScreen = false);
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp
index 6e1a1978..5948fd9a 100644
--- a/src/activities/reader/EpubReaderActivity.cpp
+++ b/src/activities/reader/EpubReaderActivity.cpp
@@ -83,9 +83,10 @@ ProgressRange getPageProgressRange(const std::shared_ptr& epub, const int
return {epub->calculateProgress(spineIndex, start), epub->calculateProgress(spineIndex, end)};
}
-bool bookmarkMatchesProgress(const BookmarkEntry& bookmark, const SavedProgressPosition& progress,
+bool bookmarkMatchesProgress(const BookmarkEntry& bookmark, const int spineIndex, const int page, const int pageCount,
const ProgressRange& pageRange) {
- if (bookmark.xpath == progress.xpath) {
+ if (bookmark.computedSpineIndex == spineIndex && bookmark.computedChapterPageCount == pageCount &&
+ bookmark.computedChapterProgress == page) {
return true;
}
@@ -1311,10 +1312,12 @@ void EpubReaderActivity::addBookmark() {
const ProgressRange pageRange = getPageProgressRange(epub, currentSpineIndex, currentPage, pageCount);
const size_t bookmarkCountBeforeToggle = cachedBookmarks.size();
- cachedBookmarks.erase(
- std::remove_if(cachedBookmarks.begin(), cachedBookmarks.end(),
- [&](const BookmarkEntry& b) { return bookmarkMatchesProgress(b, progress, pageRange); }),
- cachedBookmarks.end());
+ cachedBookmarks.erase(std::remove_if(cachedBookmarks.begin(), cachedBookmarks.end(),
+ [&](const BookmarkEntry& b) {
+ return bookmarkMatchesProgress(b, currentSpineIndex, currentPage, pageCount,
+ pageRange);
+ }),
+ cachedBookmarks.end());
if (cachedBookmarks.size() != bookmarkCountBeforeToggle) {
bookmarkRemoved = true;
currentPageBookmarked = false;
@@ -1353,12 +1356,7 @@ void EpubReaderActivity::updateBookmarkFlag() {
const ProgressRange pageRange =
getPageProgressRange(epub, currentSpineIndex, section->currentPage, section->pageCount);
currentPageBookmarked = std::any_of(cachedBookmarks.begin(), cachedBookmarks.end(), [&](const BookmarkEntry& b) {
- if (b.computedSpineIndex == currentSpineIndex && b.computedChapterPageCount == section->pageCount &&
- b.computedChapterProgress == section->currentPage) {
- return true;
- }
- const float bp = std::clamp(b.percentage, 0.0f, 1.0f);
- return bp + bookmarkProgressEpsilon >= pageRange.start && bp - bookmarkProgressEpsilon <= pageRange.end;
+ return bookmarkMatchesProgress(b, currentSpineIndex, section->currentPage, section->pageCount, pageRange);
});
}
diff --git a/src/network/html/FilesPage.html b/src/network/html/FilesPage.html
index 36f38b0e..8572b3e1 100644
--- a/src/network/html/FilesPage.html
+++ b/src/network/html/FilesPage.html
@@ -1895,15 +1895,43 @@
const fileTable = document.getElementById('file-table');
const segments = currentPath.split('/').filter(Boolean);
- const crumbs = ['🏠 Home'];
- segments.forEach(function(segment, index) {
- const path = '/' + segments.slice(0, index + 1).join('/');
- crumbs.push('' + escapeHtml(segment) + '');
- });
- // The last crumb is the current location: render as plain text, not a link.
- const lastSegment = segments.length ? escapeHtml(segments[segments.length - 1]) : '🏠 Home';
- crumbs[crumbs.length - 1] = '' + lastSegment + '';
- breadcrumbs.innerHTML = crumbs.join('›');
+ breadcrumbs.replaceChildren();
+
+ const appendSep = function() {
+ const sep = document.createElement('span');
+ sep.className = 'sep';
+ sep.textContent = '›';
+ breadcrumbs.appendChild(sep);
+ };
+
+ const appendLink = function(label, href) {
+ const link = document.createElement('a');
+ link.href = href;
+ link.textContent = label;
+ breadcrumbs.appendChild(link);
+ };
+
+ const appendCurrent = function(label) {
+ const current = document.createElement('span');
+ current.className = 'current';
+ current.textContent = label;
+ breadcrumbs.appendChild(current);
+ };
+
+ if (segments.length === 0) {
+ appendCurrent('🏠 Home');
+ } else {
+ appendLink('🏠 Home', '/files');
+ segments.forEach(function(segment, index) {
+ appendSep();
+ if (index === segments.length - 1) {
+ appendCurrent(segment);
+ } else {
+ const path = '/' + segments.slice(0, index + 1).join('/');
+ appendLink(segment, '/files?path=' + encodeURIComponent(path));
+ }
+ });
+ }
let files = [];
try {