Skip to content

Commit a58c0cc

Browse files
uxjuliazgredexkrunkosaurusTheCyberRoninUri-Tauber
authored
v1.3.4 (#284)
* fix: suppress back release from reader settings screens * fix: suppress back release from reader settings screens * fix: avoid stale back suppression after font errors * feat: index large file browser folders on SD Co-authored-by: zgredex <112968378+zgredex@users.noreply.github.com> * fix(i18n): localize home empty-state strings (crosspoint-reader#2342) * fix: avoid oversized web multi-delete requests (fixes #269) * fix: improve handling of invisible codepoints * feat: add first pass for clipping support * fix: anchor clipping highlights to text after relayout * fix: clean clipping preview text feat: add timestamps to X3 clipping exports * ci: update release workflow for catalog publishing * fix: swap reader menu navigation direction in CCW/inverted (crosspoint-reader#2321) (crosspoint-reader#2341) Co-authored-by: Uri Tauber <uritaube@gmail.com> * fix: add missing HTML 4.01 named entities (crosspoint-reader#2352) Co-authored-by: Rafael Santos <rmsantos@applaudostudios.com> * fix(koreader): resolve element XPath progress against visible body text (crosspoint-reader#2308) * fix: Ukrainian translation (crosspoint-reader#2354) Co-authored-by: KymAndriy <test@notamail.ua> * fix: lower memory guard to what jpeg decoder actually needs * fix: preserve EPUB position across font-size reindex * fix: add epub compatibility mode that falls back to upstream's parsing methods * Add grayscale preconditioning for e-ink displays Implements OEM-style settle pass between BW base frame and grayscale planes for X3 displays. The preconditioning can be applied to the full frame or windowed to a specific region in logical coordinates, which are then rotated to physical panel coordinates. This prevents particles from being set too firmly by the base refresh, allowing the weak X3 grayscale waveform to move them properly. No-op on X4 displays. * Add displayGrayscaleBase method for differential refresh Introduces a new display method that prepares the framebuffer as a base frame for grayscale overlays. On X3 panels, this uses the OEM differential base waveform (AA-pre-BW) without forcing a resync. Other panels fall back to normal display with configurable refresh mode. * update sdk * fix: regenerate fonts to handle strange codepoints * fix: avoid overlapping Calibre transfer status * feat: save EPUB reader settings per book * fix: prewarm SD fonts for clipping selection * feat: new in-reader menu * fix: improve reader menu tab focus state * fix: exit reader menu from focused tab row * fix: align clipping highlights with SD font advances * fix: enable Shift key for URL keyboard input (crosspoint-reader#2178) (crosspoint-reader#2357) * feat: add drag-and-drop to upload modal (crosspoint-reader#2290) Co-authored-by: Julia <julia@uxj.io> * feat: long-press Confirm launches KOReader sync from EPUB (crosspoint-reader#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> * feat: Support for Korean line breaks and glyph spacing (crosspoint-reader#2288) Co-authored-by: Uri Tauber <uritaube@gmail.com> * fix: protect incompatible reading stats files * chore: update docs for clippings/highlights * fix(i18n): localize home empty-state strings (crosspoint-reader#2342) * fix: add missing HTML 4.01 named entities (crosspoint-reader#2352) Co-authored-by: Rafael Santos <rmsantos@applaudostudios.com> * fix: enable Shift key for URL keyboard input (crosspoint-reader#2178) (crosspoint-reader#2357) * feat: add drag-and-drop to upload modal (crosspoint-reader#2290) Co-authored-by: Julia <julia@uxj.io> * feat: Support for Korean line breaks and glyph spacing (crosspoint-reader#2288) Co-authored-by: Uri Tauber <uritaube@gmail.com> * fix: persist reader time-left estimates for home stats * fix: prepare SD font metrics for clipping selection * fix: add stats.bin migration flow * fix: clippings not highlighting correctly with SD card fonts * fix: remove unlabeled clipping page number * fix: keep txt reader open at file boundary * feat: apply inverted filter to generated sleep screens * fix: move roundedraff clock down on home screen * fix: cppchecks * fix: export clippings with selected page number --------- Co-authored-by: zgredex <112968378+zgredex@users.noreply.github.com> Co-authored-by: Mauvis Ledford <switchstatement@gmail.com> Co-authored-by: Ronin <6971304+TheCyberRonin@users.noreply.github.com> Co-authored-by: Uri Tauber <uritaube@gmail.com> Co-authored-by: rafaelmsse <rafaelmsse@gmail.com> Co-authored-by: Rafael Santos <rmsantos@applaudostudios.com> Co-authored-by: KymAndriy <32357131+KymAndriy@users.noreply.github.com> Co-authored-by: KymAndriy <test@notamail.ua> Co-authored-by: Justin Mitchell <justin@jmitch.com> Co-authored-by: Ankit <iankit17@gmail.com> Co-authored-by: darkbublu <davidcyprianhicks@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4699ea7 commit a58c0cc

319 files changed

Lines changed: 253470 additions & 231704 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
steps:
1717
- uses: actions/checkout@v6
1818
with:
19+
token: ${{ secrets.RELEASE_PAT }}
1920
submodules: recursive
2021
fetch-depth: 0
2122

CHANGELOG.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## [1.3.4-RC] - 2026-06-17
44

55
### Added
6+
- File Browser now indexes large SD-card folders so directories with many books can be browsed without loading every filename into memory at once.
7+
- EPUB text clipping with saved highlights, clipping lists, and Kindle-style `/My Clippings.txt` export.
8+
- Per-book EPUB reader settings for font, layout, styling, and reading aids when changed from inside the reader.
69

710
### Changed
11+
- The EPUB reader menu has been updated to split out the growing menu of items into 3 separate screens for faster navigation.
12+
- The Epub reader menu now labels per-book reader settings as `Book Options` and avoids showing duplicate `Orientation` controls.
13+
- The `Inverted` sleep cover filter now flips Minimal and Reading Stats sleep screens to black text on a white background.
814

915
### Fixed
16+
- Calibre Wireless transfer status no longer stacks the last received-file message on top of the upload percentage.
17+
- EPUB chapters that run out of memory during full CrossInk layout now retry with a lighter compatibility renderer before showing a low-memory error.
18+
- EPUB reader font-size changes now restore the current chapter position by content instead of jumping far backward after re-indexing.
19+
- Reading Stats now use the reader's last live book time-left estimate instead of showing a separate fallback estimate.
20+
- Per-book reading stats now migrate compatible legacy `stats.bin` files into the `stats_v5.bin` flow instead of resetting when only the old filename exists.
21+
- Per-book reading stats now use versioned filenames so firmware builds with different schemas do not overwrite each other's stats.
22+
- Lyra Carousel Home menu rendering now avoids extra label allocations that could crash tiny builds under low memory.
23+
- TXT readers now stay open when pressing a page-turn button at the end of the file.
1024
- Long-press reader shortcuts that open another screen no longer close or confirm it again when releasing the shortcut button.
1125
- RoundedRaff's header battery icon and percentage now sit lower to avoid clipping at the top edge.
12-
- Lyra Carousel now redraws the Home header when restoring cached carousel frames so battery percentage and clock values stay current while navigating between books.
26+
- Lyra Carousel now keeps the Home header current when rendering the menu or restoring cached carousel frames, preventing stale battery and clock values while navigating between books.
27+
- Web file manager multi-delete now handles larger selections without failing after a small batch.
1328

1429
## [v1.3.3] - 2026-06-13
1530

docs/data-cache.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ The main data directory is `.crosspoint` on the SD card. It stores render caches
2727
├── opds.json # Saved OPDS servers
2828
├── koreader.json # KOReader sync credentials
2929
├── bookmarks/ # Bookmark files, one per book
30+
├── clippings/ # EPUB clipping/highlight files, one per book
3031
├── home_carousel_cache.bin # Lyra Carousel home-screen snapshot cache
3132
├── sleep_frame.bin # Temporary sleep overlay framebuffer, when used
3233
├── epub_12471232/ # Each EPUB is cached to epub_<hash>
3334
│ ├── progress.bin # Reading position (chapter, page, etc.)
34-
│ ├── stats.bin # Per-book reading stats
35-
│ ├── reader_settings.bin # Per-book reader settings
35+
│ ├── stats.bin # Legacy per-book reading stats
36+
│ ├── stats_v5.bin # Version 5 per-book reading stats
37+
│ ├── reader_settings.bin # Per-book reader settings and auto-page-turn interval
3638
│ ├── cover.bmp # Book cover image, once generated
3739
│ ├── thumb_*.bmp # Home/recent-books thumbnail images
3840
│ ├── book.bin # Book metadata, spine, table of contents, etc.
@@ -57,6 +59,17 @@ To clear EPUB/XTC render caches from the device UI without deleting settings or
5759

5860
Cache folders are path-based. Moving a book file can create a new cache directory, so the moved copy may start with fresh reading progress unless the firmware migrates the cache for that move. CrossInk migrates cache and bookmark data for the built-in move-to-Read flow and related file-browser move actions.
5961

62+
EPUB reader font, page layout, styling, and reading-aid settings normally come from the global Reader settings. If those settings are changed from inside an EPUB, CrossInk stores a per-book override in that book's `reader_settings.bin`; books without that override continue to follow the global defaults.
63+
64+
EPUB clippings and highlights live outside the EPUB render-cache folder in
65+
`/.crosspoint/clippings/`. Each book gets a binary clipping file named from the
66+
book type and the CRC32 of the book path. The same clipping record powers the
67+
in-reader highlight, the clipping list, and jump-back behavior. CrossInk also
68+
appends a Kindle-style text export to `/My Clippings.txt` on the SD-card root;
69+
that export is human-readable and append-only, so deleting a clipping in the UI
70+
removes the in-app saved clipping but does not rewrite old text already exported
71+
to `/My Clippings.txt`.
72+
6073
Cache data is cleared by supported CrossInk delete/move flows. If you remove or rename books outside CrossInk by editing the SD card directly, old cache folders may remain until you clear reading cache.
6174

6275
All-time reading stats can also be backed up outside `.crosspoint` in:

docs/file-formats.md

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
These formats describe the SD-card cache files under `/.crosspoint/epub_<hash>/`.
44
All POD fields are written in the ESP32 little-endian representation used by
5-
`Serialization.h`; strings are length-prefixed UTF-8.
5+
`Serialization.h`; strings are length-prefixed UTF-8 unless a format notes a
6+
fixed-size char buffer.
67

78
## `book.bin`
89

@@ -88,6 +89,135 @@ if (parsedSize != fileSize) {
8889
}
8990
```
9091

92+
## `reader_settings.bin`
93+
94+
### Version 2
95+
96+
Each EPUB cache directory may contain `reader_settings.bin`. Missing files mean
97+
the book uses global Reader settings and the default auto-page-turn interval.
98+
99+
Version 1 stored only:
100+
101+
- `u8 version`
102+
- `u16 autoPageTurnSeconds`
103+
104+
Version 2 stores flags before the full reader-settings snapshot. This lets the
105+
file preserve an auto-page-turn interval without forcing custom font/layout
106+
settings for the book.
107+
108+
```c++
109+
struct ReaderSettingsBin {
110+
u8 version; // 2
111+
u8 flags; // bit 0 = custom reader settings, bit 1 = custom auto-page-turn interval
112+
u16 autoPageTurnSeconds;
113+
114+
u8 fontFamily;
115+
u8 fontSize;
116+
u8 lineHeightPercent;
117+
u8 orientation;
118+
u8 screenMargin;
119+
u8 publisherPageNumbers;
120+
u8 paragraphAlignment;
121+
u8 embeddedStyle;
122+
u8 hyphenationEnabled;
123+
u8 textAntiAliasing;
124+
u8 readerDarkMode;
125+
u8 imageRendering;
126+
u8 extraParagraphSpacing;
127+
u8 forceParagraphIndents;
128+
u8 bionicReadingEnabled;
129+
u8 guideReadingEnabled;
130+
char sdFontFamilyName[64];
131+
};
132+
```
133+
134+
## `/.crosspoint/clippings/<bookType>_<crc32(path)>.bin`
135+
136+
### Version 2
137+
138+
Clipping files store the per-book EPUB clipping list used by the reader. A
139+
saved clipping is also what CrossInk renders as an in-reader highlight; there is
140+
no separate highlight file. The file lives in `/.crosspoint/clippings/` instead
141+
of the EPUB render-cache directory so clearing/rebuilding layout cache does not
142+
delete user clippings.
143+
144+
The current implementation only writes EPUB clipping files, so `bookType` is
145+
`epub`. The numeric suffix is `uzlib_crc32()` of the book's SD-card path, for
146+
example:
147+
148+
```text
149+
/.crosspoint/clippings/epub_1234567890.bin
150+
```
151+
152+
Binary layout:
153+
154+
- `[0]` version (`2`)
155+
- `[1-2]` clipping count (`uint16_t` LE, maximum `64`)
156+
- book title (`String`)
157+
- book author (`String`)
158+
- book path (`String`)
159+
- repeated clipping records:
160+
- `spineIndex` (`uint16_t` LE)
161+
- `startPage` (`uint16_t` LE)
162+
- `endPage` (`uint16_t` LE)
163+
- `pageCount` (`uint16_t` LE, at least `1`)
164+
- `startWordIndex` (`uint16_t` LE)
165+
- `endWordIndex` (`uint16_t` LE)
166+
- `wordCount` (`uint16_t` LE)
167+
- `paragraphIndex` (`uint16_t` LE, `UINT16_MAX` when unavailable)
168+
- `timestamp` (`uint32_t` LE, seconds since firmware boot when saved)
169+
- `chapterTitle` (`char[48]`, null-terminated/truncated)
170+
- selected text (`String`, truncated to `512` bytes for the in-app store)
171+
172+
CrossInk uses the stored spine/page/paragraph fields as anchors, then searches
173+
near that location for the stored clipping text after relayout. This is similar
174+
to keeping both a DOM position and a text quote in a web app: the numeric
175+
position gives a fast starting point, while the text makes jumps and highlights
176+
survive font, layout, or page-count changes when possible.
177+
178+
Creating a clipping also appends a Kindle-style export entry to
179+
`/My Clippings.txt` on the SD-card root. That text export can keep up to `2000`
180+
bytes of the selected text and is append-only. Removing a clipping from the
181+
reader deletes or rewrites only the binary clipping file; it does not remove
182+
previous entries from `/My Clippings.txt`.
183+
184+
When CrossInk moves an EPUB through its built-in move-to-Read flow, it rewrites
185+
the clipping file under the new path-derived name and removes the old one. If a
186+
book is renamed or moved outside CrossInk, the path hash changes, so the old
187+
clipping file may no longer be associated with the book until the file is moved
188+
back or the clipping store is migrated.
189+
190+
## `stats_v5.bin`
191+
192+
### Version 5
193+
194+
`stats_v5.bin` stores per-book reading statistics for stats schema version 5.
195+
Versioned filenames let firmware branches with different stats schemas keep
196+
their own per-book stats files without overwriting each other. Version 5 extends
197+
version 4 with a cached live reader book time-left estimate so Home and Reading
198+
Stats can show the same estimate the reader last computed.
199+
200+
When `stats_v5.bin` is missing, CrossInk can read the previous versioned stats
201+
filename (`stats_v4.bin` for version 5, `stats_v5.bin` after a future version 6
202+
bump) before falling back to legacy `stats.bin` files with compatible stats
203+
payloads. Future changes are always saved to the current versioned filename.
204+
205+
Binary layout:
206+
207+
- `[0]` version (`5`)
208+
- `[1-2]` `sessionCount` (`uint16_t` LE)
209+
- `[3-6]` `totalReadingSeconds` (`uint32_t` LE)
210+
- `[7-10]` `totalPagesTurned` (`uint32_t` LE)
211+
- `[11]` `isCompleted` (`uint8_t`)
212+
- `[12-13]` `avgSecondsPerForwardPage` (`uint16_t` LE)
213+
- `[14-15]` `paceSampleCount` (`uint16_t` LE)
214+
- `[16]` flags (`bit0=startDateManual`, `bit1=finishedDateManual`)
215+
- `[17-20]` `startDate` (`year uint16_t` LE, `month uint8_t`, `day uint8_t`)
216+
- `[21-24]` `finishedDate` (`year uint16_t` LE, `month uint8_t`, `day uint8_t`)
217+
- `[25-40]` `timeOfDaySeconds[4]` (`uint32_t` LE each)
218+
- `[41-68]` `dayOfWeekSeconds[7]` (`uint32_t` LE each)
219+
- `[69-72]` `estimatedTimeLeftSeconds` (`uint32_t` LE, `0` means unavailable)
220+
91221
## `section.bin`
92222

93223
### Version 40

docs/reader-features.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Open the reader menu and select **Reader Options** to adjust settings such as:
2121
- Margins
2222
- Alignment
2323
- Image rendering
24-
- Guide Dots
24+
- Bionic Reading / Guide Dots
2525
- Dark Reader Mode
2626

2727
Changes take effect immediately.
@@ -99,6 +99,25 @@ You can:
9999
- Jump back to saved locations
100100
- Delete individual bookmarks
101101

102+
## Clippings And Highlights
103+
104+
CrossInk supports EPUB text clippings from the reader. Use **Create Clipping**
105+
from the reader menu, select text on the current page, and save it.
106+
107+
A saved clipping is used in three ways:
108+
109+
- It appears as a highlight in the reader
110+
- It appears in the in-app clipping list for that book
111+
- It is appended to `/My Clippings.txt` on the SD card in a Kindle-style text format
112+
113+
The in-app clipping list is stored separately from the text export. Deleting a
114+
clipping from CrossInk removes the saved clipping and highlight from the device
115+
UI, but it does not rewrite old entries that were already appended to
116+
`/My Clippings.txt`.
117+
118+
For storage paths and binary format details, see [Data Cache](./data-cache.md)
119+
and [File Formats](./file-formats.md).
120+
102121
## Reading Stats
103122

104123
CrossInk tracks per-book reading stats automatically and aggregates them into global stats.
@@ -142,4 +161,4 @@ Examples include:
142161
- Quick access to Controls from the in-reader menu
143162
- Side-button shortcuts for changing font size or font family
144163

145-
For the full controls reference, see [Controls](./controls.md).
164+
For the full controls reference, see [Controls](./controls.md).

docs/reading-stats-sync.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ To reset only this reader's local all-time totals, delete both:
7474
/.crosspoint/global_stats.bin.bak
7575
```
7676

77-
Do not delete `/.crosspoint/epub_<hash>/stats.bin` unless you also want to remove the per-book stats for that book.
77+
Do not delete `/.crosspoint/epub_<hash>/stats.bin` or versioned files such as
78+
`/.crosspoint/epub_<hash>/stats_v5.bin` unless you also want to remove the
79+
per-book stats for that book.
7880

7981
If a sync was interrupted, a temporary `*.part` file may be left in `synced_stats/`. CrossInk ignores invalid stats files while aggregating, so it is safe to delete leftover `.part` files.
8082

lib/EpdFont/EpdFont.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ static uint8_t lookupKernClass(const EpdKernClassEntry* entries, const uint16_t
101101
}
102102

103103
int8_t EpdFont::getKerning(const uint32_t leftCp, const uint32_t rightCp) const {
104+
if (utf8IsCjkBreakable(leftCp) || utf8IsCjkBreakable(rightCp)) {
105+
return 0;
106+
}
104107
if (!data->kernMatrix) {
105108
return 0;
106109
}

0 commit comments

Comments
 (0)