1. Clear ignoreNextConfirmRelease after transferring state to child
activity, so the next Confirm press isn't silently consumed.
2. Add conditional FOOTNOTES entry to reader menu when the current
book has footnotes.
3. Guard clock-minute requestUpdate() with !isReaderActivity() to
prevent full e-ink re-renders every minute while reading.
Made-with: Cursor
Add detailed entries to MERGED.md for all 5 ported PRs with context,
changes applied, and differences from upstream. Update upstream-sync.md
tracking table with new entries and sync date.
Made-with: Cursor
When loading a section, proactively indexes all spine items belonging
to the same TOC chapter so page-turning across spine boundaries within
a chapter is instant. Uses Section::readCachedPageCount() to skip
already-cached sections and shows an "Indexing (x/y)" progress popup.
Ported from upstream PR #1172, adapted to mod architecture.
Made-with: Cursor
Long-press chapter skip now walks by TOC entries instead of spine
indices, enabling finer navigation in books with multi-chapter spines.
Status bar chapter title now uses section-level getTocIndexForPage()
for accurate subchapter display. Chapter selection passes tocIndex
back so the reader can jump directly to the right page within a spine.
Add pendingTocIndex to EpubReaderActivity for deferred cross-spine
TOC navigation, resolved after the target section loads.
Ported from upstream PRs #1143 and #1172, adapted to mod architecture.
Made-with: Cursor
Extend Section with TOC boundary tracking: buildTocBoundaries(),
getTocIndexForPage(), getPageForTocIndex(), getPageRangeForTocIndex(),
readAnchorMap(), and readCachedPageCount() for lightweight cache queries.
ChapterHtmlSlimParser now accepts a tocAnchors set and forces page breaks
at TOC anchor boundaries so each chapter starts on a fresh page.
Increment SECTION_FILE_VERSION to 19 for new TOC boundary data.
Ported from upstream PRs #1143 and #1172, adapted to mod architecture.
Made-with: Cursor
Adapted from upstream PR #1325 (not yet merged).
When focused on the tab bar (selectedSettingIndex == 0), the confirm
button label now shows the name of the next category instead of
the generic "Toggle" text.
If/when #1325 is merged upstream, this commit should be dropped
during the next sync and the upstream version used instead.
Made-with: Cursor
Adapted from upstream PR #1320 (not yet merged).
Replaces scattered free()/delete cleanup with RAII Cleanup struct
that guarantees resource release on all return paths. Changes
rowCount from uint16_t* to uint32_t* to prevent overflow on
large images.
If/when #1320 is merged upstream, this commit should be dropped
during the next sync and the upstream version used instead.
Made-with: Cursor
Adapted from upstream PR #1329 (not yet merged).
Adds ReaderUtils.h with shared orientation, page-turn detection,
refresh cycle, and anti-aliased rendering utilities. Refactors
EpubReaderActivity and TxtReaderActivity to use shared implementations
instead of duplicated inline code.
If/when #1329 is merged upstream, this commit should be dropped
during the next sync and the upstream version used instead.
Made-with: Cursor
Full upstream resync: mod/master-resync was rebuilt from upstream/master
with all mod features re-applied to the new ActivityManager architecture.
This merge records the history connection but keeps the resync content.
- Fix fp4 fixed-point misuse in PlaceholderCoverGenerator (advanceX is 12.4
fixed-point, not pixels) causing only first letter of each word to render
- Remove duplicate silentIndexNextChapterIfNeeded() call from loop() that
blocked UI before render, preventing the indexing indicator from showing
- Fix indexing icon Y position to align within the status bar
- Add ignoreNextConfirmRelease to EpubReaderChapterSelectionActivity so
long-press confirm release doesn't immediately select the first TOC item
- Reload recent books after cache deletion in HomeActivity and clear stale
ignoreNextConfirmRelease flag to fix "no open books" and double-press bugs
Made-with: Cursor
- Add isValidThumbnailBmp(), generateInvalidFormatCoverBmp(), and
generateInvalidFormatThumbBmp() methods to Epub class for validating
BMP files and generating X-pattern marker images when cover extraction
fails (e.g., progressive JPG).
- Restore prerender block in EpubReaderActivity::onEnter() that checks
for missing cover BMPs (fit + cropped) and thumbnail BMPs at each
PRERENDER_THUMB_HEIGHTS size, showing a "Preparing book..." popup
with progress. Falls back to PlaceholderCoverGenerator, then to
invalid-format marker BMPs as last resort.
Made-with: Cursor
- Move Letterbox Fill setting to immediately after Sleep Screen Cover
Filter in the Display section where it is more relevant.
- Add Indexing Display setting to Display section with three modes:
Popup (default), Status Bar Text ("Indexing..."), and Status Bar Icon
(hourglass).
- Restore silent indexing logic in EpubReaderActivity::render() that
proactively indexes the next chapter when on a text-only page near
the end of the current chapter (non-popup mode only).
- Draw indexing indicator in renderStatusBar() when silentIndexingActive
is set and the user has chosen text or icon mode.
Made-with: Cursor
- Replace scattered book management actions (Archive, Delete, Reindex,
Delete Cache) with single "Manage Book" entry that opens
BookManageMenuActivity as a submenu.
- Replace scattered dictionary actions (Lookup Word, Lookup History,
Delete Dict Cache) with single "Dictionary" entry that opens new
DictionaryMenuActivity submenu.
- Add long-press Confirm (700ms) to open Table of Contents directly
from the reader, bypassing the menu.
- Add STR_DICTIONARY i18n key and regenerate I18nKeys.h/I18nStrings.h.
Made-with: Cursor
- Add clock rendering to BaseTheme::drawHeader() and LyraTheme::drawHeader()
after battery, before title. Respects clockFormat (OFF/AM-PM/24H) and
clockSize (Small/Medium/Large) settings.
- Fix PlaceholderCoverGenerator splitWords() to treat newlines, tabs, and
carriage returns as whitespace delimiters (not just spaces), preventing
one-character-per-line output from EPUB metadata with embedded newlines.
- Remove drawBorder() from placeholder covers since the UI already draws
its own frame around book cards.
Made-with: Cursor
- Update open-x4-sdk submodule to 9f76376 (BatteryMonitor ESP-IDF 5.x compat)
- Add RTC_NOINIT bounds check for logHead in Logging.cpp
- Add drawTextRotated90CCW to GfxRenderer for dictionary UI
- Add getWordXpos() accessor to TextBlock for dictionary word selection
- Fix bare include paths (ActivityResult.h, RenderLock.h) across 10 files
- Fix rvalue ref binding in setResult() lambdas (std::move pattern)
- Fix std::max type mismatch (uint8_t vs int) in EpubReaderActivity
- Fix FsFile forward declaration conflict in Dictionary.h
- Restore StringUtils::checkFileExtension() and sortFileList()
- Restore RecentBooksStore::removeBook()
Made-with: Cursor
Re-add KOReaderSyncActivity PUSH_ONLY mode (PR #1090):
- SyncMode enum with INTERACTIVE/PUSH_ONLY, deferFinish pattern
- Push & Sleep menu action in EpubReaderMenuActivity
- ActivityManager::requestSleep() for activity-initiated sleep
- main.cpp checks isSleepRequested() each loop iteration
Wire EndOfBookMenuActivity into EpubReaderActivity:
- pendingEndOfBookMenu deferred flag avoids render-lock deadlock
- Handles all 6 actions: ARCHIVE, DELETE, TABLE_OF_CONTENTS,
BACK_TO_BEGINNING, CLOSE_BOOK, CLOSE_MENU
Add book management to reader menu:
- ARCHIVE_BOOK, DELETE_BOOK, REINDEX_BOOK actions with handlers
Port silent next-chapter pre-indexing:
- silentIndexNextChapterIfNeeded() proactively indexes next chapter
when user is near end of current one, eliminating load screens
Add per-book letterbox fill toggle in reader menu:
- LETTERBOX_FILL cycles Default/Dithered/Solid/None
- Loads/saves per-book override via BookSettings
- bookCachePath constructor param added to EpubReaderMenuActivity
Made-with: Cursor
- Add drawPixelGray to GfxRenderer for letterbox fill rendering
- Add PRERENDER_THUMB_HEIGHTS to UITheme for placeholder cover generation
- Add [env:mod] build environment to platformio.ini
- Implement sleep screen letterbox fill (solid/dithered) with edge
caching in SleepActivity, including placeholder cover fallback
- Add Clock settings category to SettingsActivity with timezone,
NTP sync, and set-time actions; replace CalibreSettings with
OpdsServerListActivity; add DynamicEnum rendering support
- Add long-press book management to RecentBooksActivity
Made-with: Cursor
HomeActivity: Add mod features on top of upstream ActivityManager pattern:
- Multi-server OPDS support (OpdsServerStore instead of single URL)
- Long-press recent book for BookManageMenuActivity
- Long-press Browse Files to open archive folder
- Placeholder cover generation for books without covers
- startActivityForResult pattern for manage menu
EpubReaderMenuActivity: Replace upstream menu items with mod menu:
- Add/Remove Bookmark, Lookup Word, Go to Bookmark, Lookup History
- Table of Contents, Toggle Orientation, Toggle Font Size
- Close Book, Delete Dictionary Cache
- Pass isBookmarked and currentFontSize to constructor
- Show current orientation/font size value inline
EpubReaderActivity: Add mod action handlers:
- Bookmark add/remove via BookmarkStore
- Go to Bookmark via EpubReaderBookmarkSelectionActivity
- Dictionary word lookup via DictionaryWordSelectActivity
- Lookup history via LookedUpWordsActivity
- Delete dictionary cache
- Font size toggle with section re-layout
- Close Book action
ActivityResult: Add fontSize field to MenuResult
Made-with: Cursor
## Summary
Follow-up
https://github.com/crosspoint-reader/crosspoint-reader/pull/1145
- Fix log not being record without USB connected
- Bump release log to INFO for more logging details
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? **NO**
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary: Enable footnote anchor navigation in EPUB reader
This PR extracts the core anchor-to-page mapping mechanism from PR #1143
(TOC fragment navigation) to provide immediate footnote navigation
support. By merging this focused subset first, users get a complete
footnote experience now while simplifying the eventual review and merge
of the full #1143 PR.
---
## What this extracts from PR #1143
PR #1143 implements comprehensive TOC fragment navigation for EPUBs with
multi-chapter spine files. This PR takes only the anchor resolution
infrastructure:
- Anchor-to-page mapping in section cache: During page layout,
ChapterHtmlSlimParser records which page each HTML id attribute lands
on, serializing the map into the .bin cache file.
- Anchor resolution in `EpubReaderActivity`: When navigating to a
footnote link with a fragment (e.g., `chapter2.xhtml#note1`), the reader
resolves the anchor to a page number and jumps directly to it.
- Section file format change: Bumped to version 15, adds anchor map
offset in header.
---
## Simplified scope vs. PR #1143
To minimize conflicts and complexity, this PR differs from #1143 in key
ways:
* **Anchors tracked**
* **Origin:** Only TOC anchors (passed via `std::set`)
* **This branch:** All `id` attributes
* **Page breaks**
* **Origin**: Forces new page at TOC chapter boundaries
* **This branch:** None — natural flow
* **TOC integration**
* **Origin**: `tocBoundaries`, `getTocIndexForPage()`, chapter skip
* **This branch:** None — just footnote links
* **Bug fix**
* **This branch:** Fixed anchor page off-by-1/2 bug
The anchor recording bug (recording page number before `makePages()`
flushes previous block) was identified and fixed during this extraction.
The fix uses a deferred `pendingAnchorId` pattern that records the
anchor after page completion.
---
## Positioning for future merge
Changes are structured to minimize conflicts when #1143 eventually
merges:
- `ChapterHtmlSlimParser.cpp` `startElement()`: Both branches rewrite
the same if `(!idAttr.empty())` block. The merged version will combine
both approaches (TOC anchors get page breaks + immediate recording;
footnote anchors get deferred recording).
- `EpubReaderActivity.cpp` `render()`: The `pendingAnchor` resolution
block is positioned at the exact same insertion point where #1143 places
its `pendingTocIndex` block (line 596, right after `nextPageNumber`
assignment). During merge, both blocks will sit side-by-side.
---
## Why merge separately?
1. Immediate user value: Footnote navigation works now without waiting
for the full TOC overhaul
2. Easier review: ~100 lines vs. 500+ lines in #1143
3. Bug fix included: The page recording bug is fixed here and will carry
into #1143
4. Minimal conflicts: Structured for clean merge — both PRs touch the
same files but in complementary ways
---
### AI Usage
Did you use AI tools to help write this code? _**< YES >**_ Done by
Claude Opus 4.6
## Summary
* **What is the goal of this PR?** Potential stack buffer overflow from
untrusted ZIP entry name length
* **What changes are included?** If nameLen >= 256 , this writes past
the stack buffer. Risk: memory corruption/crash on malformed EPUB/ZIP.
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** PARTIALLY **_ Issue
identified by AI
Description
This Pull Request introduces Turkish language support to CrossPoint
Reader firmware.
Key Changes:
- Translation File: Added lib/I18n/translations/turkish.yaml with 315
translated
string keys, covering all system UI elements.
- I18N Script Update: Modified scripts/gen_i18n.py to include the "TR"
abbreviation mapping for Turkish.
- System Integration: Regenerated I18N C++ files to include the new
Language::TR
enum and STRINGS_TR array.
- UI Availability: The language is now selectable in the Settings menu
and
correctly handles Turkish-specific characters (ç, ğ, ı, ö, ş, ü).
- Documentation: Updated docs/i18n.md to include Turkish in the list of
supported languages.
Testing:
- Verified the build locally with PlatformIO.
- Flashed the firmware to an Xteink X4 device and confirmed the Turkish
UI
renders correctly.
---
### AI Usage
Did you use AI tools to help write this code? Yes Gemini
---------
Co-authored-by: Baris Albayrak <baris@Bariss-MacBook-Pro.local>
Co-authored-by: Barış Albayrak <barisa@pop-os.lan>
## Summary
* **What is the goal of this PR?**
* Improve and add the latest missing Swedish translations.
* **What changes are included?**
* Added missing Swedish translations in
`lib\I18n\translations\swedish.yaml`
## Additional Context
* (none)
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
added Romanian ranslations from recent commits.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
## Summary
**What is the goal of this PR?**
This change avoids the pattern of creating a `std::string` using
`.substr` in order to compare against a file extension literal.
```c++
std::string path;
if (path.length() >= 4 && path.substr(path.length() - 4) == ".ext")
```
The `checkFileExtension` utility has moved from StringUtils to
FsHelpers, to be available to code in lib/. The signature now accepts a
`std::string_view` instead of `std::string`, which makes the single
implementation reusable for Arduino `String`.
Added utility functions for commonly repeated extensions.
These changes **save about 2 KB of flash (5,999,427 to 5,997,343)**.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
**What is the goal of this PR?**
Quick follow up to #1291, adding Polish translations suggested by
@th0m4sek
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?**
Update relative paths to correctly navigate from .skills/ directory to
project root by adding ../ prefix to file references.
* **What changes are included?**
.skills/SKILL.md
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< YES >**_
## Summary
* **What is the goal of this PR?**
Add missing Catalan strings.
* **What changes are included?**
Changes on catalan.yaml file only.
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? PARTIALLY
## Summary
**What is the goal of this PR?**
Avoid building cache path strings twice, once to check existence of the
file and a second time to delete the file.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?** Extend missing / amend existing
German translations
* **What changes are included?** German.yaml
## Additional Context
---
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com>
## Summary
**What is the goal of this PR?** Add a user setting to decide image
support: display, show placeholder instead, supress fully
Fixes#1289
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
## Summary
**Goal of this PR**
Add an **"In-scope — technically not supported"** section to `SCOPE.md`.
This clarifies hardware/UX limitations (e.g., clock support) and is
intended to reduce recurring feature requests on topics already
discussed.
Based on discussions in #287 and #626.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
## Summary
* Enable `DESTRUCTOR_CLOSES_FILE` flag
* We're never intending to not close files, so if we accidentally leave
them open as they're destructured, this will help close them.
## Additional Context
* As spotted in
https://github.com/crosspoint-reader/crosspoint-reader/pull/869, there
are cases where we were accidentally not closing files
Looks to use about 5K of flash.
```
RAM: [=== ] 31.5% (used 103100 bytes from 327680 bytes)
Flash: [======= ] 68.9% (used 4513220 bytes from 6553600 bytes)
```
```
RAM: [=== ] 31.5% (used 103100 bytes from 327680 bytes)
Flash: [======= ] 68.9% (used 4518498 bytes from 6553600 bytes)
```
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? No
## Summary
* **What changes are included?**
New Ukrainian localization strings
## Additional Context
auto turn functionality
https://github.com/crosspoint-reader/crosspoint-reader/pull/1219
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO **_
The folder picker had no visible way to navigate to a parent directory.
Add a ".." list item (shown when not at root) so users can go up by
selecting it, matching standard file-picker conventions.
Made-with: Cursor
After an OPDS download completes, show a prompt screen instead of
immediately returning to the catalog. The user can choose to open the
book for reading or go back to the listing. A live countdown (5s, fast
refresh) auto-selects the configured default; any button press cancels
the timer. A per-server "After Download" setting controls the default
action (back to listing for backward compatibility).
Made-with: Cursor
Servers are sorted by a persistent sortOrder field (ties broken
alphabetically). On-device editing uses a new NumericStepperActivity
with side buttons for ±1 and face buttons for ±10. The web UI gets
up/down arrow buttons and a POST /api/opds/reorder endpoint.
Made-with: Cursor
## Summary
Ref discussion:
https://github.com/crosspoint-reader/crosspoint-reader/pull/1222#discussion_r2865402110
Important note that this is a bug-for-bug fix. In reality, this branch
`WiFi.status() == WL_CONNECTED` is pretty much a dead code because the
entry point of these 2 activities don't use wifi.
It is better to refactor the management of network though, but it's
better to be a dedicated PR.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? **NO**
## Summary
* **What is the goal of this PR?**
- Improve and add the latest missing Spanish translations
* **What changes are included?**
- Add missing spaces and remove extra unneeded ones (spaces at the end
of certain strings and others, i.e. the one introduced in the string
`Smart Device`; actually, `SmartDevice` is the correct Calibre plugin
name)
- Normalise the use of caps in certain strings
- Adapting the translation to the one found in related third-party
software (i.e. Spanish translation for the word `plugin` in Calibre is
`complemento`)
- Shortening some translations to make them smaller and fit better in
screen
- Rewording ambiguous translations (i.e. `Volver a inicio` could mean to
go back to Home, but also to go back to the first page of the current
book, so I changed it for a more specific action, `Volver al menú
Inicio`)
## Additional Context
* **Missing spaces caused a lack of clarity**
- My main motivation for this PR was the following:
<details>
<summary>Screenshots:</summary>
In English:

In Spanish:

</details>
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>