Commit Graph
59 Commits
Author SHA1 Message Date
Justin Mitchell e69241ef54 Reverts UI font change and adds setting for AA sd fonts 2026-07-06 18:59:25 -04:00
Tom-Inge Larsen 3e1dd31e53 feat(reader): End of Book next-book suggestions (#2499) (#2532) 2026-07-04 23:12:59 +03:00
Tom-Inge LarsenandClaude Opus 4.8 e4d3981469 fix(reader): correct slider side-button direction and legend on X3 (#2402) (#2428)
## Summary

* **What is the goal of this PR?** Fixes #2402. On the X3, the "Time to
Sleep"
picker's 5-minute side buttons were inverted (left increased, right
decreased)
  and the on-screen legend didn't match the physical buttons.
* **What changes are included?**
  * Flip the large-step (±5 min) direction on X3 so the left side button
decreases and the right increases, matching the layout. X4 is unchanged.
* Add a device-specific step-hint string
(`STR_SLEEP_TIMER_STEP_HINT_X3`)
selected via `gpio.deviceIsX3()`. X3 shows `+/-: 1 min Side buttons: 5
min`;
    X4 keeps the original `Left/Right: 1 min  Up/Down: 5 min`.
* Same fix applied to both slider pickers that read the raw up/down side
    buttons: the Time-to-Sleep picker and the "Go to %" picker
(`EpubReaderPercentSelectionActivity`), each with its own X3 hint string
    (`STR_SLEEP_TIMER_STEP_HINT_X3`, `STR_PERCENT_STEP_HINT_X3`).

## Additional Context

* **Root cause:** the X3's side buttons sit one on each edge of the
screen
(power on top), whereas the X4 has a vertical up/down rocker on the
right
edge. So `BTN_UP` is physically the *left* button on X3 but the *top*
button
  on X4. The picker mapped up→+5 / down→−5 unconditionally, which reads
naturally on the X4 rocker but inverted on the X3's left/right buttons.
The
  static legend ("Up/Down: 5 min") had the same X3-only mismatch.
* The codebase already detects the device at runtime and handles this
layout
difference elsewhere (e.g. `LyraTheme::drawSideButtonHints` draws "Up on
left,
Down on right" for X3), so this reuses the same `gpio.deviceIsX3()`
signal.
* Swept the codebase for other side-button sliders:
`ClockOffsetActivity` already
uses the `Next/Previous` (`ButtonNavigator`) abstraction, which resolves
to
left=decrement / right=increment on X3, so it needs no change. List/page
navigation (Wi-Fi, KOReader sync, BMP viewer) and the keyboard cursor
are also
unaffected. The two slider pickers above were the only ones using raw
up/down.
* **X4 is untouched** — same direction, same legend wording as before.
* **Tested on X3 hardware:** left side button now decreases, right
increases, and
  the legend matches. X4 not yet tested on device (no unit on hand); its
  behaviour and strings are unchanged from master.
* **Translations:** the new `STR_SLEEP_TIMER_STEP_HINT_X3` and
`STR_PERCENT_STEP_HINT_X3` were added to all 26 languages, but the
non-English
  ones are AI-generated and would benefit from a native-speaker pass —
particularly Hebrew (RTL ordering with a leading `+/-`), Kazakh, and
Vietnamese.

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:06:31 -04:00
Julia 487613b082 fix: split inverted orientation label from color filter label (#2421)
## Summary

* **What is the goal of this PR?** 
* Fix the “Inverted” translation so reader orientation and color/filter
inversion can use separate labels.
* **What changes are included?**
* Adds `STR_ORIENTATION_INVERTED` for the inverted portrait orientation
option.
* Updates the reader orientation setting to use
`STR_ORIENTATION_INVERTED` instead of reusing `STR_INVERTED`.
* Leaves `STR_INVERTED` for the sleep cover filter and tilt page-turn
mode
  * Adds the new orientation string across all 26 locale YAML files.

## Additional Context

* The original issue was found by a user in German, where `STR_INVERTED`
was translated as `Hochformat 180°`, which made sense for orientation
but not for color filters.
* This is a UI-label-only change. It does not change persisted
orientation values or settings behavior.
* Reviewer note: non-English wording may still benefit from
native-speaker review, especially for the new orientation-specific
labels and to verify the interchangeable usage between inverted color
and inverted tilt page turn direction.
---

### 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 >**_
2026-06-25 00:47:06 -04:00
Uri TauberandJulia Nguyen 1db1442319 fix: several bookmarks UX improvments (#2372)
## Summary

This PR enhances the EPUB reader's bookmark system with two
complementary improvements: a per-page bookmark indicator icon and
toggle behavior on the existing long-press action.

---

### What Changed

**Bookmark Toggle (was: add-only)**

The long-press Confirm action now toggles bookmarks rather than always
adding. `addBookmark()` checks whether a bookmark with the same xpath
already exists in the in-memory cache:
- If found → removes it and shows "Bookmark removed."
- If not found → adds it and shows "Bookmark added."

A new `STR_BOOKMARK_REMOVED` translation string was added to support the
removal message.

**Bookmark Icon Indicator**

A `BookmarkIcon` is now drawn at the top-right corner of the page
whenever the current page has a bookmark. `updateBookmarkFlag()` is
called at render time to determine whether the current page is
bookmarked.

**In-Memory Bookmark Cache**

Bookmarks are now loaded into `cachedBookmarks` on `onEnter()` rather
than being re-read from disk on every toggle. All subsequent add/remove
operations work against this cache and flush to disk, avoiding redundant
file reads on each bookmark action.

**Faster bookmarks list**

Previously, calculating "page X/Y" for each entry required decompressing
the entire spine item. We now persist `si`/`pc`/`pp` (spine index, page
count, and page progress) in the bookmark JSON when saving, and restore
them when loading. This avoids the expensive `toCrossPoint()` loop in
`onEnter()`, significantly reducing the cost of initializing the
bookmarks list.

---

### Files Changed

- `EpubReaderActivity.cpp` — `addBookmark()` toggle logic,
`updateBookmarkFlag()` (new), icon rendering in `renderContents()`,
cache initialization in `onEnter()`
- `EpubReaderActivity.h` — new fields: `currentPageBookmarked`,
`bookmarkRemoved`, `cachedBookmarks`; new method declaration
`updateBookmarkFlag()`

---

### 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 >**_

---------

Co-authored-by: Julia Nguyen <julia@uxj.io>
2026-06-23 14:55:01 -04:00
Paul Delestrac b1131795d8 feat: add live font preview pane to font selection screen (#2349)
## Summary
* Adds a live font preview pane to the font selection screen so users
can see how a font looks before committing to it.
* Changes
* A preview pane occupying the top 30% of the font selection screen,
rendering sample pangram text in the previewed font
* A two-step confirm flow: first press (enter button) previews the font,
second press selects it
* Back restores the original font settings, so browsing has no side
effects
* Layout dimensions cached in `onEnter()` to avoid redundant
recalculation between `loop()` and `render()`

## Additional Context
* Preview sample text is hardcoded English; didn't want to use AI for
translation as I would not be able to verify the output in most
languages...
* The preview pane reduces visible list height; this is compensated by
passing the reserved height into `getNumberOfItemsPerPage`

---
### AI Usage
Did you use AI tools to help write this code? **PARTIALLY**
AI use for assisting in coding and in writing the PR description.
2026-06-18 18:16:28 -04:00
darkbubluandClaude Opus 4.7 d4069aeae5 feat: long-press Confirm launches KOReader sync from EPUB (#1808)
## Summary
- Hold the menu (Confirm) button for ≥1s while reading an EPUB to launch
the existing `KOReaderSyncActivity` directly — replaces the three-step
path (open reader menu → scroll to Sync → confirm) with a single
gesture.
- Reuses `ReaderUtils::GO_HOME_MS` (same 1s threshold used by long-press
Back) and the existing `KOREADER_STORE.hasCredentials()` guard.
- Adds a Controls picker **"Long-press Menu"** (`longPressMenuFunction`,
default **Bookmark**) that **cycles through the available functions**
bound to the long-press gesture: `KOSync → Disabled → Bookmark`. The
field name and `LONG_PRESS_MENU_FUNCTION` enum are intentionally general
so future actions (dictionary lookup, table of contents, etc.) can be
appended without another schema migration. The setting is **not** a
binary toggle.
- Existing menu Sync entry still works — both call sites share one
extracted helper (`launchKOReaderSync`); no logic duplication.
- Short-press Confirm release is gated on duration so the reader menu
does not also open after a long press that *acts*, mirroring the
existing long-press Back pattern.
- **No-credentials fall-through:** `launchKOReaderSync()` now returns
whether it acted. When the function is set to KOSync but no KOReader
credentials are stored, the long-press is a no-op that **falls through
to open the reader menu** — so the menu stays reachable instead of the
hold silently swallowing the gesture. The release is only suppressed
when sync actually launched or surfaced a save error.

## Test plan
- [x] `pio run` succeeds clean for the `default` ESP32-C3 environment.
- [x] On-device, value **KOSync**, valid KOReader credentials:
long-press Confirm ≥1s → sync screen launches; get + update progress
return HTTP 200; release does **not** also open the reader menu; returns
to the same page.
- [x] On-device, value **KOSync**, **no** credentials: long-press
Confirm falls through and **opens the reader menu** (regression fix);
short-press also opens the menu.
- [x] On-device: menu → Sync still launches the same screen (shared
helper) and syncs (200/200).
- [x] On-device, value **Bookmark**: long-press drops a bookmark and
does **not** also open the menu.
- [x] On-device, value **Disabled**: long-press Confirm opens the menu
on release; no sync, no bookmark.
- [x] On-device: long-press Back still goes to the file browser
(unchanged path).
- [x] Heap: epub is released before the TLS handshake (frees ~16 KB);
min free heap stayed ~84 KB during sync, well above the safe floor. No
panics/OOM across the session.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-17 17:10:26 +03:00
Lorenzo Santini 9c12b90737 feat: add power button short press for quick access to footnotes (#1658) 2026-06-08 14:45:41 -04:00
Uri Tauber b12839d1d4 chore: Move opendyslexic from flash to SD (#2231) 2026-06-01 13:49:57 -04:00
Wilhelm Schuster b229428a17 chore: remove unused sleep timer interval strings (#2223) 2026-06-01 00:26:17 +03:00
WuTofuandJulia ebf0413fe1 feat: add custom sleep timer picker (#2206)
## Summary

* **What is the goal of this PR?**
Mainly fixes #2196, but also allows users to set a sleep time of 1~30
min or to never sleep.

* **What changes are included?**

1. Revert changes from #1948 and #2137 (Sorry @Uri-Tauber)
2. Cherrypick changes from
https://github.com/uxjulia/CrossInk/commit/0cb91c3dd1c63f0559d4392fadc4a8340af5bdde
and
https://github.com/uxjulia/CrossInk/commit/4e6bd634b6ede51ff1a7107ba3e8efcd152bc6ee
for a timeout interval picker UI, the custom sleep time feature, and
migrate the existing setting in v1.3.0, `sleepTimeout`, to
`sleepTimeoutMinutes` (Thanks! @uxjulia)
3. Add a "Never" at the far right of the timeout interval picker

---

### 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**_

---------

Co-authored-by: Julia <julia@uxj.io>
2026-05-30 08:28:07 -04:00
36a3a0cc3a feat: epub bookmarks (#1337)
Co-authored-by: vedi0boy <nate@origin8publishing.com>
Co-authored-by: Uri Tauber <uritaube@gmail.com>
2026-05-27 15:52:01 -04:00
Joseph DiGiovanni 67973166e3 feat: Allow disabling side buttons in reader (#2105) 2026-05-25 18:00:27 -04:00
Stefan Blixten Karlsson b56d3d853d fix: update Swedish translations and remove unused strings (#2090) 2026-05-25 16:30:22 -04:00
Leopoldo Pla Sempere 0f021ea5f1 fix: normalize Wi-Fi spelling across remaining locales (#2094) 2026-05-23 21:01:12 +03:00
Julia c44555007b feat(settings): modify "Page as Sleep Screen" to "Quick Resume" options (#2089)
## Summary

**What is the goal of this PR?**

Adds a clearer Quick Resume sleep-screen flow. The previous “Page as
Sleep Screen” behavior is now exposed as a dedicated `Sleep Screen >
Quick Resume `option, with the timeout-only behavior controlled by a
renamed `Quick Resume on Timeout `setting.

**What changes are included?**

- Adds `Quick Resume` as a new `Sleep Screen` option.
- Renames the old `Page as Sleep Screen` setting to `Quick Resume on
Timeout`.
- Changes that setting’s choices from `Never / After Timeout / Always`
to `OFF / ON`.
- Makes `Quick Resume on Timeout = ON` equivalent to the old `After
Timeout` behavior.
- Makes `Sleep Screen > Quick Resume` equivalent to the old `Always`
behavior.
- Automatically forces `Quick Resume on Timeout` to `ON` when `Sleep
Screen` is set to `Quick Resume`.
- Renames internal setting references from `seamlessSleepScreen` to
`quickResumeSleepScreen`.
- Updates translations for the renamed setting label.

**Additional Context**

- This is mostly a settings/labeling restructure around existing
behavior, not a new rendering path.
- The runtime quick-resume behavior still uses the existing saved
framebuffer / last-screen sleep flow.
- Review focus areas:
  - Sleep entry behavior from manual sleep vs timeout sleep.
- The automatic dependency where selecting `Sleep Screen > Quick Resume`
sets `Quick Resume on Timeout` to `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? _**< YES >**_

---
**New `Quick Resume` option for `Sleep Screen` will automatically set
`Quick Resume on Timeout` to `ON`**:
<img width="480" height="800" alt="quick resume"
src="https://github.com/user-attachments/assets/94c553fd-a122-47a8-add9-f29694f55566"
/>

**Example where a different sleep screen setting like `Cover` can be
used in combination with the `Quick Resume on Timeout` setting**:
<img width="480" height="800" alt="cover + quick resume"
src="https://github.com/user-attachments/assets/dd18ce18-230b-4b78-808b-ac85f5e7d5d8"
/>
2026-05-20 22:43:48 -04:00
Eloren1andClaude Sonnet 4.6 41e6e15229 feat: Seamless sleep/wake screens for displaying book pages during deep sleep (#2064)
<img width="605" height="454" alt="image"
src="https://github.com/user-attachments/assets/bfd84afe-3b58-436e-9a5d-539af3ec3d4e"
/>

Actualized "Last" sleep screen setting from previous PRs, rebranded as a
~~`Seamless Sleep`~~ `Page as Sleep Screen` option with more
improvements.


https://github.com/user-attachments/assets/59029ba6-007e-4841-abfa-f680d7e98b79

---

New option: `Page as Sleep Screen` - `Never (default)`, `After Timeout`,
`Always`

When enabled, it seamlessly sleeps on timeout or power off, making a
fast refresh for the moon icon. When waking up, we still show the last
page, instead of the boot screen, making it fully seamless.

I tried different icons such as "refresh arrow" and others, but they
looked not as nice as 3 simple dots.

With this mode, the device turns off 4 seconds faster. And has 6 seconds
less delay when turning back on. Much more responsive.

Previously, even a 10-minute timeout sometimes wasn't enough, and I'd
worry about seeing the book cover. It's now easier to use a shorter
sleep timeout: if I get distracted during a reading session but don't
want to stop, the new screen is much more inviting to come back to.

---

Did you use AI tools to help write this code? _**PARTIALLY**_.

---

Test v1.3.0 firmware.bin file
[download](https://github.com/user-attachments/files/28015193/firmware.zip)

Based on PRs #410 and #495

Closes #400, #1649

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:45:28 -04:00
Jackson beb7876ccf feat: Make page turn naturally follow orientation (#2023) 2026-05-19 23:13:27 +03:00
KemoNine df53faab91 feat: allow removing book from recent list (#2045)
## Summary

Add ability to long press 'confirm' on a book in the recent books list
to be prompted to remove it from the list.

---

### 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, Claude*

---------
2026-05-18 22:16:33 -04:00
KemoNine 6a98c2d865 feat: add setting that allows removing books from recent list when read (#2043) 2026-05-18 21:13:41 -04:00
Justinian cfd3a381ed feat: X3 clock display with DS3231 RTC and NTP sync (#1612) 2026-05-18 21:06:56 -04:00
KemoNine 06d28d6ffa feat: port crossink 'read book move' feature to crosspoint (#2032) 2026-05-18 12:39:50 -04:00
WuTofu 061c7688a4 chore: add 3-minute sleep option (#1948) 2026-05-18 00:17:24 +03:00
WuTofu bc6e090aa8 feat: separate into "Download All" and "Update All" in font manager (#1955)
## Summary

* **What is the goal of this PR?**  
Separate the font manager's combined `Download / Update All` action into
separate `Download All` and `Update All` rows

* **What changes are included?**  
- Updated multiple i18n translation files to add `STR_UPDATE_ALL` and
adjust `STR_DOWNLOAD_ALL` text.
- Added separate handlers: `downloadAll()` for fonts that have been
installed and `updateAll()` for fonts with updates.
  - `Download All` and `Update All` are only shown when applicable.

---

### 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**_
2026-05-12 12:17:01 -05:00
WuTofu a0037ced2b feat: add font family deletion functionality (#1919)
## Summary

* **What is the goal of this PR?**

1. Adds font family deletion support to the font download activity so
users can remove installed font families directly from the download
list.
2. Address an issue where a font family manually deleted in the file
browser by user will show as "update" in the `FontDownloadActivity`
 

* **What changes are included?**
* Updated button hint label to show `Delete` when deletion is available.
  * Added confirmation before deleting a selected installed font family.
* Refreshed font registry in `fetchAndParseManifest` (for the second
goal above)

---

### 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**_
2026-05-11 15:45:38 +02:00
Vincent Politzer 4e5e2fe40a feat: focus reading (#1670)
## Summary

This PR introduces **Focus Reading**, a generic implementation of
artificial fixation points (similar to Bionic Reading) designed to
improve reading speed and focus by bolding the initial characters of
words. This is achieved by dynamically bolding characters during
indexing.

<img width="500" alt="Focus Reading on X3"
src="https://github.com/user-attachments/assets/94a632a5-82da-47be-957c-538b35bf84d9"
/>

### Implementation Details
#### Core Text Engine (`ParsedText`)
- Modified `ParsedText::addWord` to implement a custom bolding
algorithm. It uses a 45% ratio for bolding, with a minimum of 1
character and a maximum of 9.
- UTF-8 Safety: Integrated `utf8NextCodepoint` to ensure character
counting and string slicing occur at safe byte boundaries, preventing
corruption of multi-byte characters (e.g., accented letters or smart
quotes).
- Intelligent Tokenization: This correctly identifies and separates
"word" characters (letters, apostrophes, hyphens) from "non-word"
characters (numbers, brackets, smart quotes).
- Formatting Preservation: The logic ensures that punctuation is not
"stolen" for the bolding count and that existing styles (like italics or
underlines) are preserved across the bold/regular split.
- Processing at indexing stage reduces CPU load at render-time and
ensures layout/fit is unaffected.
- Split details are tracked with `wordIsFocusSuffix`. After splitting
and layout, suffixes are merged back into their preceding word entries
to prevent a doubling of RAM usage.

#### Settings and UI
- Version Management: Bumped `SECTION_FILE_VERSION` to `21`
- Global Settings: Added `focusReadingEnabled` to `CrossPointSettings`.
- User Interface: Added a new toggle in the "Reader" section of the
settings menu, positioned after the "Embedded Style" option.
- Localization: Added the `STR_FOCUS_READING` string

#### Plumbing
- Plumbed the `focusReadingEnabled` boolean through
`EpubReaderActivity`, `Section`, and `ChapterHtmlSlimParser` to ensure
the user's setting reaches the `ParsedText` constructor during chapter
indexing.

## Additional Context

### Files Changed
- `lib/Epub/Epub/ParsedText.h / .cpp`: Core fixation logic and UTF-8
tokenization.
- `lib/Epub/Epub/Section.h / .cpp`: Cache header updates and
invalidation logic.
- `lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h / .cpp`: Plumbing the
setting to text blocks.
- `src/CrossPointSettings.h`: Data persistence for the new setting.
- `src/SettingsList.h`: UI toggle implementation.
- `src/activities/reader/EpubReaderActivity.cpp`: Handling settings
changes during reading sessions.
- `lib/I18n/translations/*.yaml`: UI strings.

---

### 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**_
2026-05-10 20:02:14 +02:00
Wylan SwetsandClaude Sonnet 4.6 e64155ed63 fix: free Epub RAM and simplify KOSync navigation via ActivityManager (#1860)
## Summary

* **What is the goal of this PR?** Fix KOSync failing with "Network
error" on large/complex EPUBs, and simplify the sync navigation flow by
removing the callback/result pattern.

* **What changes are included?**

This PR combines the approaches from #1855 and #1760 into a single,
cleaner solution:

  **Memory fix (from #1855):**
- `EpubReaderActivity` pre-computes the local KOReader position and
chapter name, then explicitly releases `epub` and `section` before
launching `KOReaderSyncActivity`. This frees ~65KB measured on device,
giving the TLS handshake sufficient heap. The root cause was
`MBEDTLS_ERR_X509_ALLOC_FAILED` (-0x2880) when a 3-cert chain consumed
~48KB during the handshake with only ~50KB available.
- `KOReaderSyncActivity` no longer receives a `shared_ptr<Epub>` at
construction — it lazy-loads the Epub after TLS only if remote progress
is found (`ensureEpubLoaded()`).
- Added `MIN_HEAP_FOR_TLS = 55000` guard in `KOReaderSyncClient` —
returns `LOW_MEMORY` early if aggregate free heap is too low before
attempting a TLS connection.

  **Navigation simplification (from #1760):**
- Replaced `startActivityForResult` + callback with
`activityManager.replaceActivity` / `activityManager.goToReader`.
Progress is saved to `progress.bin` before the epub is released
(cancel/upload paths) and in `saveProgressAndReturn` (apply remote
path). The reader re-launches from the saved position naturally via
`goToReader`, eliminating the need to reload the epub in a callback.
- Extracted `ReaderUtils::saveProgress()` as a shared helper used by
both `EpubReaderActivity` and `KOReaderSyncActivity`.
- Added `STR_SAVE_PROGRESS_FAILED` to all 22 language files for the case
where writing the synced position to SD fails.

  **Orientation fix (found during device testing):**
- `EpubReaderActivity::onExit()` resets the renderer to portrait before
destruction. With `replaceActivity` the reader is fully torn down before
KOSync starts, so KOSync was always rendering in portrait even when
reading in landscape. Fixed by calling `ReaderUtils::applyOrientation`
in `KOReaderSyncActivity::onEnter()`.

## Additional Context

Heap measurements on device (large EPUB with complex CSS):

| Metric | Before | After |
|---|---|---|
| Heap before Epub release | 88,156 bytes | — |
| Heap after Epub release | — | 153,892 bytes (+65,736) |
| Heap at TLS handshake | ~50,000 bytes (fails) | ~116,384 bytes
(passes) |
| Min-free-ever during sync session | 2,600 bytes | 33,052 bytes |
| TLS result | `MBEDTLS_ERR_X509_ALLOC_FAILED` | HTTP 200 |

Tested on device: sync from inside a large EPUB in both portrait and
landscape, cancel, apply remote progress, upload local progress.

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:01:31 -05:00
7993b2bb97 feat: add SD card font support with on-device download and web management
Add a complete SD card font subsystem that enables users to install and
use custom fonts beyond the three built-in families. This combines the
back-end firmware support (#1327) with the font configuration, build
pipeline, CI distribution, and user-facing management UI (#1392).

Core font system:
- Custom .cpfont binary format (v4) with multi-style support (regular,
  bold, italic, bold-italic) packed into a single file per size
- On-demand glyph loading from SD card with two-pass prewarm rendering
  to bulk-read glyphs per page, achieving near-flash performance for
  Latin text (~697ms vs ~681ms) and viable CJK rendering (~32% slower)
- Persistent advance cache for layout measurement without SD I/O
- Overflow ring buffer for glyph cache misses during rendering
- Memory-conscious design: only advance tables kept in RAM; glyph
  bitmaps, kern tables, and ligatures loaded on demand from SD

Font management:
- On-device WiFi download from GitHub Releases with manifest-based
  discovery, install/update detection, and progress UI
- Web interface font upload, listing, and deletion via /fonts page
- Manual SD card copy to /fonts/ or /.fonts/ directories
- Font selection integrated into Settings > Reader > Font Family

Build pipeline:
- Declarative YAML config (sd-fonts.yaml) as single source of truth
  for the 17-family font library (serif, sans, mono, accessibility)
- Python converter (fontconvert_sdcard.py) for TTF/OTF to .cpfont with
  FreeType rasterization, class-based kerning, and ligature extraction
- Parallel build orchestrator with variable font instance extraction
- CI workflow publishing versioned + stable releases to a dedicated
  crosspoint-fonts repository with auto-incrementing revision tags
- Centralized version constants (cpfont_version.py) shared across
  build tooling and CI, with firmware headers as manual sync points

Additional fixes:
- CJK characters no longer get hyphens inserted at line breaks
- Advance table eliminates 30+ second stalls during CJK section
  indexing for paragraphs with >512 unique codepoints

Closes #930

Co-authored-by: Zach Nelson <zach@zdnelson.com>
Co-authored-by: Justin <itsthisjustin@users.noreply.github.com>
Co-authored-by: jpirnay <jens@pirnay.com>
Co-authored-by: mcrosson <kemonine@kemonine.info>
2026-05-08 21:50:06 -05:00
Chun Ming LeeandZach Nelson 29fd29f537 feat: Status bar for XTC files (#1849)
## Summary
Add ability to show a status bar for 1-bit XTC files  (closes #1848) 

Overlays a status bar (similar style to that for epubs) over the image.
Defaults to hidden, users can set to show and either top or bottom of
the screen within "Customize Status Bar" reader settings.

I've leaned toward a simple overlay approach rather than trying anything
clever e.g. resizing the original image or allowing for shifting the
image around. I think it's more straight forward for the user to create
a buffer zone when generating the XTC files.

For 2-bit image files, it'd require more work to handle the multiple
render passes so I'm holding off on that.

Sample from a Japanese text XTC
<img width="400" height="600" alt="image"
src="https://github.com/user-attachments/assets/03ac67cf-acec-4b49-969f-73e3656760ce"
/>

### 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 | PARTIALLY | NO
>**_
YES - Codex

---------

Co-authored-by: Zach Nelson <zach@zdnelson.com>
2026-05-08 14:34:42 -05:00
pablohc b463966045 Revert "feat(sleep-screen): add sleep screen orientation setting" (#1877)
Reverts crosspoint-reader/crosspoint-reader#1748

The images in landscape mode are borked.

Cover:
<img width="696" height="1004" alt="image"
src="https://github.com/user-attachments/assets/354950f3-d3db-4c9d-a771-60f0868666d2"
/>

Sleep Screen:
<img width="688" height="1044" alt="image"
src="https://github.com/user-attachments/assets/3d675b55-93c1-4b5f-9e98-10e18e54c53e"
/>
2026-05-08 12:31:02 -05:00
xiro-codesandTravis Davis e3fb3bba37 feat(sleep-screen): add sleep screen orientation setting (#1748)
Co-authored-by: Travis Davis <me@tdavis.dev>
2026-05-08 11:41:06 +03:00
Eliz efa4f71a68 feat: Set sleep cover from BMP viewer (#1104) 2026-05-05 12:23:20 +03:00
eunchurn 5717374e4b feat(update): SD-card firmware update + X3 bootloader compatibility (#1786) 2026-05-04 19:51:20 -04:00
MaciekandЕгор Мартынов 2e2ea6a9e8 feat: Page turn button orientation change (#1069)
Co-authored-by: Егор Мартынов <martynovegorOF@yandex.ru>
2026-05-04 08:56:02 +03:00
5d2e5596b4 feat: X3 gyroscope-based tilt page turning via QMI8658 IMU (#1636)
Co-authored-by: justinian <juicecutlus@gmail.com>
Co-authored-by: Vincent Politzer <vincent@skipwithjoy.com>
2026-04-29 15:29:33 -04:00
b8a51522ca feat(theme): add roundedraff theme and fix sleep cover crop grid artifacts (#918)
## Summary
- Add new `roundedraff` theme.
- Fix sleep screen artifact where book cover showed grid lines in
`Cover` mode with `Crop`.

## Additional Context
## Key changes
- Added `src/components/themes/roundedraff/` theme implementation.
- Updated sleep cover rendering logic in:
  - `src/activities/boot_sleep/SleepActivity.cpp`
  - `src/activities/boot_sleep/SleepActivity.h`
- Improved cover generation/regeneration paths in:
  - `lib/Epub/Epub.cpp`
  - `lib/Epub/Epub.h`
  - `lib/Txt/Txt.cpp`
  - `lib/Txt/Txt.h`

## Verification
- Sleep screen set to Book cover
- Sleep screen cover mode set to Crop
- Confirmed grid artifacts no longer appear on cover sleep screen.

### 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 >**_

---------

Co-authored-by: CaptainFrito <yzq6x5zypy@privaterelay.appleid.com>
Co-authored-by: Zach Nelson <zach@zdnelson.com>
Co-authored-by: Uri Tauber <142022451+Uri-Tauber@users.noreply.github.com>
2026-04-27 19:01:22 +03:00
Arthur TazhitdinovandCopilot 1cf2239742 feat: Support for multiple OPDS servers (#1209)
## Summary

* Add support for configuring and using multiple OPDS servers, replacing
the previous single-server limitation. Closes
https://github.com/crosspoint-reader/crosspoint-reader/issues/1178
* New OpdsServerStore singleton (modeled after WifiCredentialStore) that
persists up to 8 OPDS servers to /.crosspoint/opds.json with MAC-based
password obfuscation.
* One-time migration from legacy single-server fields in
CrossPointSettings to the new store on first boot.
* New OpdsServerListActivity for the device UI — works in two modes: a
settings list (add/edit/delete servers) and a picker (select which
server to browse). When only one server is configured, the picker is
skipped automatically.
* Renamed CalibreSettingsActivity → OpdsSettingsActivity for clarity. It
now edits individual OpdsServer entries (name, URL, username, password,
delete).
* OpdsBookBrowserActivity now receives an OpdsServer at construction and
uses its credentials for all fetches/downloads, and shows the server
name in the header.
* HttpDownloader::fetchUrl and downloadToFile accept optional per-call
username/password parameters instead of reading from global settings.
* REST API endpoints on CrossPointWebServer: GET /api/opds, POST
/api/opds, POST /api/opds/delete — passwords are never exposed over the
API (only a hasPassword flag), and omitting the password field on update
preserves the existing one.
* Web UI (SettingsPage.html) with dynamic OPDS server management cards —
add, edit, save, and delete servers from the browser.
<img width="932" height="906" alt="SCR-20260416-stvu"
src="https://github.com/user-attachments/assets/a8f18d84-4204-46a0-bb31-b73d24b3255f"
/>


## Additional Context

* The OpdsServerStore JSON format and obfuscation scheme are identical
to WifiCredentialStore, so the same JsonSettingsIO infrastructure
handles both.
* The web API uses POST /api/opds/delete instead of DELETE /api/opds
because the ESP32 WebServer doesn't support the DELETE method with a
request body.
* Existing single-server configurations are migrated automatically — no
user action required. After migration the legacy CrossPointSettings
fields are cleared so it only runs once.
* The HttpDownloader changes are backward-compatible: the credential
parameters default to empty strings, so existing callers are unaffected.

---

### 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 >**_

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-21 18:35:41 -05:00
Zach Nelson c5f82709c0 fix: Replaced Bookerly with Noto Serif for licensing reasons (#1736)
## Summary

Fixes #258. Bookerly is not licensed for use in CrossPoint. Switched to
Google's open Noto Serif font.

---

### 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**_
2026-04-21 17:51:07 -05:00
pablohc 1a145fe085 fix: keyboard feedback #1644 (#1697)
Addresses reviewer feedback from #1644:
- **Localize keyboard hint strings** — 13 hardcoded English strings
replaced with \`tr()\` macro (\`STR_KB_HINT_*\`), making them
translatable across all 22 languages (fallback to English when not yet
translated)
- **Deduplicate \`Lyra3CoversMetrics\`** — now derives from
\`LyraMetrics\` via lambda copy, overriding only \`homeCoverTileHeight\`
and \`homeRecentBooksCount\` (eliminates ~30 duplicated metric fields)
- **Unify keyboard drawing in \`BaseTheme\`** — \`drawTextField\` and
\`drawKeyboardKey\` overrides removed from \`LyraTheme\`; variability
controlled via \`keyboardKeyCornerRadius\` metric (0=Base, 6=Lyra).
Unified text field padding to 6, adopted Lyra's secondary label draw
order (main first, then secondary)
- **Add URL-optimized keyboard layout** — \`urlLayout\` with \`:\` and
\`/\` replacing \`=\` and \`,\` for easier URL input without switching
to SYM mode
---

### 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 **_
2026-04-20 12:47:57 -05:00
Xuan-Son Nguyen 243ae8b408 feat: show crash reason on boot (#1453)
## Summary

If the system reboots from crash, display the reason and tell user to
include `crash_report.txt` file.

To test this, simply add an `assert(false)` somewhere inside the code.


![screenshot-74453.bmp](https://github.com/user-attachments/files/26160373/screenshot-74453.bmp)

---

### 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**
2026-04-13 15:23:45 -05:00
bdeshi 9c11f3e4a2 feat: enable manual screen refresh on power button short press (#1626)
## Summary

Adds an option to allow manual screen refresh on power button short
press.

## Additional Context

there's an option to refresh the screen after a set number of pages. but
sometimes a manual refresh is needed to clear up stale ink pixels. this
works everywhere both in and out of reading mode.

resolves #550 

---

### 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**_, to
understand the project structure.
2026-04-12 22:31:26 +03:00
fa2a3d2539 feat: add OPDS search support & next/prev page navigation (#1462)
## Summary

**What is the goal of this PR?**
Adds OPDS search support, allowing users to search a catalog directly
from the book browser when the server exposes an OpenSearch template.

**What changes are included?**
- `OpdsParser`: parses the OpenSearch template URL from feed-level
`<link rel="search">` elements and exposes it via `getSearchTemplate()`
- `OpdsBookBrowserActivity`: fetches and stores the search template
after each feed load; shows a Search hint on the Left button when a
template is available; launches the existing `KeyboardEntryActivity` for
query input; URL-encodes the query and fetches the result feed
- Absolute search result URLs are handled correctly in `fetchFeed`
(skips prepending the server base URL)
- A `consumeConfirm` guard prevents the Confirm release that submits the
keyboard from immediately triggering a book download on the first
browsing frame after search results load

## Additional Context

- Search is silently unavailable if the server does not advertise an
OpenSearch template — no UI change in that case
- Tested against a Calibre-Web OPDS endpoint which exposes `<link
rel="search" type="application/opensearchdescription+xml">`
- The inline URL encoder in `performSearch` was necessary as
`StringUtils` has no such utility; worth considering extracting to
`StringUtils` in a follow-up
- No new dependencies introduced

---

### 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: kira <rammah@tuta.io>
Co-authored-by: Justin Mitchell <justin@jmitch.com>
2026-04-12 12:24:27 -04:00
Stefan Blixten Karlsson 99721c081b fix: Reduce flash usage by cleaning up I18n translations (#1401)
## Summary

* **What is the goal of this PR?** 
Removing no longer used i18n keys/string, to reduce (~28k) used flash
space.
To correct to swedish translations for `STR_FONT_SIZE` and
`STR_KOREADER_SYNC`.

* **What changes are included?**
   `lib\I18n\translations\*`

## 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? _**NO**_
2026-03-18 09:59:03 -05:00
jpirnay f9286709d1 feat: Show hidden directories in browser (#1288)
## Summary

* **What is the goal of this PR?** Add setting to display hidden files /
directories in filebrowser / web file browser
* **What changes are included?**

## 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 >**_
2026-03-18 09:57:58 -05:00
jpirnay ce0b439aa3 feat: User setting for image display (#1291)
## 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 >**_
2026-03-03 09:59:06 -06:00
Lev Roland-Kalb 76681201bf fix: Hide unusable button hints when viewing empty directory (#1253)
## Summary

* **What is the goal of this PR?**

Increase accuracy of button hints and text description in the file
browser when viewing empty directory.
 
* **What changes are included?**

Adjusted button label hint rendering logic in file browser to hide the
"Open", "Up", and "Down" hints when the they are not available due to an
empty directory.

I also changed the NO_BOOKS_FOUND string to NO_FILES_FOUND and updated
translations. File browser shows more than just books so seeing "No
Books Found" really doesn't make sense.

## Additional Context

Very Simple change, here is what that looks like on my device.

<img width="1318" height="879" alt="Untitled (7)"
src="https://github.com/user-attachments/assets/6416c8c8-795d-41a5-9b9f-28d2c26666a0"
/>

---

### 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**_
2026-03-01 13:10:25 +11:00
5e95d9a36f feat: Long Click for File Deletion through File Browser (#909)
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)

Allow users to better manage their epub library by offloading unwanted
or finished books and other files. Resolves #893

* **What changes are included?**

Added Delete Book shortcut in the fil browser. Delete function
implements the new ConfirmationActivity to show file name and solicit
user interaction before either returning to the file browser on a press
of the back button, or proceeding to delete. Delete function then
deletes the file and returns user to the file browser menu at the
current directory. Video of it working on my machine attached here:


https://github.com/user-attachments/assets/329b0198-9e97-45ad-82aa-c39894351667


## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).

Certainly potential risks associated with file deletion. Please let me
know if there are any concerns that need to be better addressed. I think
this is a very good feature to have to go along with the new screenshots
so you don't get stuck with a bunch of extra files on your device. Also
I did add this to the user guide.

---

### 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**_

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Егор Мартынов <martynovegorOF@yandex.ru>
Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com>
Co-authored-by: Zach Nelson <zach@zdnelson.com>
2026-02-28 10:58:10 -06:00
GenesiaW 3b4f2a1129 feat: Auto Page Turn for Epub Reader (#1219)
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
- Implements auto page turn feature for epub reader in the reader
submenu

* **What changes are included?**
  - added auto page turn feature in epub reader in the submenu
  - currently there are 5 settings, `OFF, 1, 3, 6, 12` pages per minute

## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).
  - Replacement PR for #723 
- when auto turn is enabled, space reserved for chapter title will be
used to indicate auto page turn being active
  - Back and Confirm button is used to disable it

---

### 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 (mainly code
reviews)**_
2026-02-27 22:42:41 +03:00
09cef70709 fix: clarity issue with ambiguous string SET (#1169)
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)

Fixes a clarity issue regarding the translation string `STR_SET`. The
issue lies in the fact that the english word can have different
meanings.

The only time the string is used is in the language selectio screen,
where it has the meaning of _selected_. (As in _The language has been
**set** to French_).

Another meaning can be _configured_. (As in _The KOReader username has
been __set__). This is the meaning many of the translations have taken.
The reason that the string is right above `STR_NOT_SET` (which is meant
as _not configured_).

With this PR I propose to explicitly use the term "_Selected_". There
are two good reasons for this:
+ it removes the confusion and the misleading translations
+ it is consistent with the button label `Select`, communicating the
link between the two (the row will be marked `Selected` if you press the
buttpn `Select`. Much clearer than now)

* **What changes are included?**

Removed the unused strings and added translations for the new string
`STR_SELECTED` for the languages I know.

tagging the translators for feedback:
fr: @Spigaw @CaptainFrito 
de: @DavidOrtmann
cs: @brbla 
pt: @yagofarias
it: @andreaturchet @fargolinux
ru: @madebykir @mrtnvgr 
es: @yeyeto2788 @Skrzakk @pablohc 
sv: @dawiik
ca: @angeldenom 
uj: @mirus-ua 
be: @dexif 

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).

the Issue was introduced in #1020. Previously, if a language was
selected it was marked with `[ON]` (`STR_ON_MARKER`). I considered
reverting it back to that, but the solution I described above seemed
superior.

---

### 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: Егор Мартынов <martynovegorOF@yandex.ru>
Co-authored-by: Mirus <mirusim@gmail.com>
2026-02-27 11:45:05 -06:00
Uri TauberandArthur Tazhitdinov 30d8a8d011 feat: slim footnotes support (#1031)
## Summary
**What is the goal of this PR?** Implement support for footnotes in epub
files.
It is based on #553, but simplified — removed the parts which
complicated the code and burden the CPU/RAM. This version supports basic
footnotes and lets the user jump from location to location inside the
epub.

**What changes are included?**
- `FootnoteEntry` struct — A small POD struct (number[24], href[64])
shared between parser, page storage, and UI.
- Parser: `<a href>` detection (`ChapterHtmlSlimParser`) — During a
single parsing pass, internal epub links are detected and collected as
footnotes. The link text is underlined to hint navigability.
Bracket/whitespace normalization is applied to the display label (e.g.
[1] → 1).
- Footnote-to-page assignment (`ChapterHtmlSlimParser`, `Page`) —
Footnotes are attached to the exact page where their anchor word
appears, tracked via a cumulative word counter during layout, surviving
paragraph splits and the 750-word mid-paragraph safety flush.
- Page serialization (`Page`, `Section`) — Footnotes are
serialized/deserialized per page (max 16 per page). Section cache
version bumped to 14 to force a clean rebuild.
- Href → spine resolution (`Epub`) — `resolveHrefToSpineIndex()` maps an
href (e.g. `chapter2.xhtml#note1`) to its spine index by filename
matching.
- Footnotes menu + activity (`EpubReaderMenuActivity`,
`EpubReaderFootnotesActivity`) — A new "Footnotes" entry in the reader
menu lists all footnote links found on the current page. The user
scrolls and selects to navigate.
- Navigate & restore (`EpubReaderActivity`) — `navigateToHref()` saves
the current spine index and page number, then jumps to the target. The
Back button restores the saved position when the user is done reading
the footnote.

  **Additional Context**

**What was removed vs #553:** virtual spine items
(`addVirtualSpineItem`, `isVirtualSpineItem`), two-pass parsing,
`<aside>` content extraction to temp HTML files, `<p class="note">`
paragraph note extraction, `replaceHtmlEntities` (master already has
`lookupHtmlEntity`), `footnotePages` / `buildFilteredChapterList`,
`noterefCallback` / `Noteref` struct, and the stack size increase from 8
KB to 24 KB (not needed without two-pass parsing and virtual file I/O on
the render task).
 
**Performance:** Single-pass parsing. No new heap allocations in the hot
path — footnote text is collected into fixed stack buffers (char[24],
char[64]). Active runtime memory is ~2.8 KB worst-case (one page × 16
footnotes × 88 bytes, mirrored in `currentPageFootnotes`). Flash usage
is unchanged at 97.4%; RAM stays at 31%.
   
**Known limitations:** When clicking a footnote, it jumps to the start
of the HTML file instead of the specific anchor. This could be
problematic for books that don't have separate files for each footnote.
(no element-id-to-page mapping yet - will be another PR soon).

---

### AI Usage

Did you use AI tools to help write this code? _**< PARTIALLY>**_
Claude Opus 4.6 was used to do most of the migration, I checked manually
its work, and fixed some stuff, but I haven't review all the changes
yet, so feedback is welcomed.

---------

Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com>
2026-02-26 08:47:34 -06:00