## 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>
When a row has a subtitle, position the icon at the midpoint between the title and subtitle text centers instead of aligning only with the title. This prevents icons from appearing too high when subtitles are present.
Refactor UI font generation to support different rendering modes based on size. Small UI fonts (12pt) use 1-bit rendering for crisp display and reduced flash usage. Large UI fonts (14pt) now use 2-bit compressed rendering to stay smooth when scaled up on touch/high-density boards. Extract font generation logic into reusable generate_ui_font() function.
Introduce gen_icons.sh to regenerate UI icons from a manifest file using the FreeInk SDK's Lucide icon set. Add icons.manifest mapping UI icons to Lucide names. Refactor vertical swipe list scrolling into a reusable wasListScroll() helper method used by both reader chapter selection and settings activities. Add static assertion to ensure ThemeMetrics scaling stays in sync with struct changes.
Removed the getFontXHeight method from GfxRenderer interface. Updated documentation to clarify that getFontCapHeight returns the 'H' glyph height for optical alignment, and improved the comment for getTextVisualCenterOffset to be more concise.
Implement orientation-aware icon rendering using freeink::Icon format that applies rotation transforms per-pixel. Add wasListScroll helper for touch-based list navigation with swipe gestures. Switch vertical centering from x-height to cap-height for better visual alignment with capital-led UI labels.
Introduces uiScale() method to apply per-board UI scaling based on device type (1.0 for button devices, >1 for touch devices to make UI elements finger-sized). Adds scaledMetrics member to store scaled theme metrics that are returned by getMetrics().
Simplify and clarify documentation for touch gesture detection (back gesture, item taps, long-press) and board detection flags. No functional changes, just improved comment readability.
Implements a press-and-hold gesture in the center touch zone (400ms threshold) that opens the reader menu, mirroring the Confirm button behavior. The center third of the screen is now reserved for this menu gesture, while left and right thirds continue to handle page turns. Applied to both EPUB and XTC readers.
Implements tap zones on touch devices for page back/forward navigation and press-and-hold actions, mirroring physical button behavior. Adds isXteinkDevice() helper to distinguish Xteink X3/X4 boards from touch devices. The sunlight fading fix setting is now exclusive to Xteink devices, while touch devices get the new touch reader controls toggle instead.
Separate the default constructor from the parameterized constructor to allow value-initialization of Rect arrays without routing through an explicit constructor. The parameterized constructor remains explicit to prevent implicit int-to-Rect conversions. Member variables now use in-class initializers.
Changed the fallback corner gesture detection to use logical (orientation-mapped) coordinates instead of native panel coordinates. This ensures the gesture area remains in the visual top-left corner across all screen orientations, particularly for screens without a header or Back button target like the reader.
Make the entire continue-reading card (cover, title, and gray area) clickable instead of just the cover photo. The clickable area now spans the full tile width rather than just the cover width plus padding.
Implement touch-down event detection to provide immediate visual feedback when touching list items, mirroring button navigation behavior. Add long-press detection (500ms threshold) to distinguish between tap and hold gestures. Apply touch-down selection updates to file browser, home menu, and recent books activities.
Implements tapToLogical() to convert normalized touch coordinates to orientation-aware logical screen coordinates. Adds TouchRegistry hit testing for interactive UI elements and header back button. Touch gestures now work correctly across all screen orientations (Portrait, Landscape, etc.) by inverting the rotateCoordinates transform.
Add dynamic I2C bus selection for battery gauge to support boards with multiple I2C controllers (e.g., Sticky uses Wire1 to avoid conflicts with GT911 touch). Fix logging on ESP32-S3 USB-Serial-JTAG by using esp_rom_printf instead of Serial, which incorrectly reports disconnected state.
Replace hardcoded X3-specific I2C pins and frequencies with values from the active board profile. Add support for boards with I2C fuel gauges (X3, LilyGo, Sticky) and ADC-based battery sensing (X4, M5Paper). Skip ADC setup when batteryAdc is unassigned to prevent GPIO mux faults.
Prevent I2C pin collision on S3/ESP32 boards by skipping the X3/X4 fingerprint probe. The probe's I2C pins (GPIO20/0) conflict with onboard peripherals like the BQ27220 gauge and reconfigure strapping pins. Force deviceType to X4 for non-C3 builds.
Change SPI pre-claim logic to only apply on C3/Xteink hardware where EPD_* pins are hardcoded. Other boards (M5Paper, Sticky) need to initialize SPI from BoardConfig::ACTIVE pins to avoid incorrect pin assignments for display and SD card.
## Summary
This PR adds the [Libre Baskerville
](https://github.com/impallari/Libre-Baskerville) font family to the
fonts available for download in CrossPoint.
## Additional Context
Baskerville is a classic font family, widely available in some form or
the other in most ereaders and widely used in publishing and in the
academy.
Libre Baskerville is a libre (SIL Open Font License v1) implementation
of it, which makes it possible for CrossPoint to ship it without issues.

---
### AI Usage
Did you use AI tools to help write this code? _**NO**_
## Summary
Non-functional cleanup: `ChapterHtmlSlimParser::` prefix is redundant
when calling `applyDirectionToEntry` within `ChapterHtmlSlimParser`
methods.
---
### AI Usage
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?** Prevent heap memory exhaustion caused
by thousands of machine-generated anchor IDs injected by epub converters
like Kobo KePub.
* Fix#2292.
* **What changes are included?**
* **Element-type filter (Layer 1):** Introduced the
`isNonNavigableInlineElement()` function in `ChapterHtmlSlimParser.cpp`
to automatically skip recording IDs on `<span>` elements, as they are
purely inline wrappers used for tracking and lack navigable meaning.
* **Hard cap (Layer 2):** Added the `MAX_ANCHORS_PER_CHAPTER = 1024`
constant to act as a fallback safety net against unbounded heap growth
from unknown future ID-injection patterns on non-span elements.
* **TOC Safety net:** Ensured that IDs matching known Table of Contents
(TOC) entries explicitly bypass both the `<span>` filter and the
1024-anchor cap, guaranteeing that chapter page-break and core
navigation logic are never compromised.
---
### 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 >**_
Add cppcheck-suppress comments for knownConditionTrueFalse warnings related to CROSSPOINT_SHOW_BUTTON_HINTS, which is a build-time configuration switch that may appear as a constant condition during static analysis.